Skip to content

Commit

Permalink
feat: provide new package 'asset' (#163)
Browse files Browse the repository at this point in the history
* can be installed as '@cord.network/asset'
* updated README to reflect the new commands to run the test
* updated augument-api to reflect the new changes to manage Did and ChainSpace based asset interactions

Signed-off-by: Amar Tumballi <amar@dhiway.com>
  • Loading branch information
amarts authored Jan 18, 2024
1 parent ebea41a commit eb8ccf1
Show file tree
Hide file tree
Showing 25 changed files with 912 additions and 235 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ Follow the instructions under the topic - "Run the node."

```
$ yarn tsx demo/src/func-test.ts
$ yarn demo-statement
$ yarn tsx demo/src/network-score-test.ts
$ yarn demo-network-score
$ yarn demo-asset
```
The output of these runs are self-explanatory. For reference of how this is structured,
Expand Down
201 changes: 201 additions & 0 deletions demo/src/asset-tx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import * as Cord from "@cord.network/sdk";
import { addNetworkMember } from "./utils/createAuthorities.js";
import { createAccount } from "./utils/createAccount.js";
/*
import {
buildFromAssetProperties,
failproofSubmit,
buildFromAssetIssueProperties,
buildFromAssetTransferProperties,
} from "./utils/assets.js";
import { AssetTypeOf, IAssetProperties } from "./utils/asset-types.js";
*/
import { createDid } from "./utils/generateDid";

const { NETWORK_ADDRESS, ANCHOR_URI } = process.env;

async function main() {
const networkAddress = NETWORK_ADDRESS ?? 'ws://127.0.0.1:9944';
const anchorUri = ANCHOR_URI ?? '//Alice';

// Temporarily suppress console.log
let originalConsoleLog = console.log;
console.log = () => {};
Cord.ConfigService.set({ submitTxResolveOn: Cord.Chain.IS_IN_BLOCK });
await Cord.connect(networkAddress);
const api = Cord.ConfigService.get("api");
// Restore console.log
console.log = originalConsoleLog;
console.log(`\nOn-Chain Assets & Transactions `);

// Step 1: Setup Identities
console.log(`\n❄️ Identities`);
const networkAuthorityIdentity = Cord.Utils.Crypto.makeKeypairFromUri(
anchorUri,
"sr25519"
);

// const { account: issuerIdentity } = createAccount();
// Create issuer DID
const { mnemonic: issuerMnemonic, document: issuerDid } = await createDid(
networkAuthorityIdentity
)
const issuerKeys = Cord.Utils.Keys.generateKeypairs(issuerMnemonic)
console.log(
`🏛 Issuer (${issuerDid?.assertionMethod![0].type}): ${issuerDid.uri}`
)

const { mnemonic: holderMnemonic, document: holderDid } = await createDid(
networkAuthorityIdentity
)
const holderKeys = Cord.Utils.Keys.generateKeypairs(holderMnemonic)
console.log(
`🏛 Holder (${holderDid?.assertionMethod![0].type}): ${holderDid.uri}`
)
const { mnemonic: holder2Mnemonic, document: holder2Did } = await createDid(
networkAuthorityIdentity
)
console.log(
`🏛 Holder2 (${holder2Did?.assertionMethod![0].type}): ${holder2Did.uri}`
)

const { account: apiIdentity } = createAccount();
console.log(`🏦 API Provider (${apiIdentity.type}): ${apiIdentity.address}`);

// await addNetworkMember(networkAuthorityIdentity, issuerIdentity.address);
await addNetworkMember(networkAuthorityIdentity, apiIdentity.address);
console.log("✅ Identities created!");

// Step 3: Create a new Chain Space
console.log(`\n❄️ Chain Space Creation `)
const spaceProperties = await Cord.ChainSpace.buildFromProperties(
issuerDid.uri
)
console.dir(spaceProperties, {
depth: null,
colors: true,
})

console.log(`\n❄️ Chain Space Properties `)
const space = await Cord.ChainSpace.dispatchToChain(
spaceProperties,
issuerDid.uri,
networkAuthorityIdentity,
async ({ data }) => ({
signature: issuerKeys.authentication.sign(data),
keyType: issuerKeys.authentication.type,
})
)
console.dir(space, {
depth: null,
colors: true,
})
console.log('✅ Chain Space created!')

console.log(`\n❄️ Chain Space Approval `)
await Cord.ChainSpace.sudoApproveChainSpace(
networkAuthorityIdentity,
space.uri,
100
)
console.log(`✅ Chain Space Approved`)

// Step 2: Create assets on-chain
let assetProperties: Cord.IAssetProperties = {
assetType: Cord.AssetTypeOf.art,
assetDesc: "Asset - " + Cord.Utils.UUID.generate(),
assetQty: 10000,
assetValue: 100,
assetTag: "Tag - " + Cord.Utils.UUID.generate(),
assetMeta: "Meta - " + Cord.Utils.UUID.generate(),
};

console.log(`\n❄️ Asset Properties - Created by Issuer `);
console.dir(assetProperties, {
depth: null,
colors: true,
});

const assetEntry = await Cord.Asset.buildFromAssetProperties(
assetProperties,
issuerDid.uri,
space.uri,
);

console.log(`\n❄️ Asset Transaction - Created by Issuer `);
console.dir(assetEntry, {
depth: null,
colors: true,
});

const extrinsic = await Cord.Asset.dispatchCreateToChain(
assetEntry,
networkAuthorityIdentity,
space.authorization,
async ({ data }) => ({
signature: issuerKeys.authentication.sign(data),
keyType: issuerKeys.authentication.type,
}),
)

console.log("✅ Asset created!");

// Step 3: Issue Asset to Holder
console.log(`\n❄️ Issue Asset to Holder - Issuer Action `);
const assetIssuance = await Cord.Asset.buildFromIssueProperties(
assetEntry.uri,
holderDid.uri,
1,
issuerDid.uri,
space.uri,
);

console.dir(assetIssuance, {
depth: null,
colors: true,
});
const issueExtrinsic = await Cord.Asset.dispatchIssueToChain(
assetIssuance,
networkAuthorityIdentity,
space.authorization,
async ({ data }) => ({
signature: issuerKeys.authentication.sign(data),
keyType: issuerKeys.authentication.type,
}),
)

// Step 4: Transfer Asset to New Owner
console.log(`\n❄️ Transfer Asset to New Owner (Holder2) - Holder Action `);

const assetTransfer = await Cord.Asset.buildFromTransferProperties(
assetIssuance.uri,
holder2Did.uri,
holderDid.uri,
);

console.dir(assetTransfer, {
depth: null,
colors: true,
});

const transferExtrinsic = await Cord.Asset.dispatchTransferToChain(
assetTransfer,
networkAuthorityIdentity,
async ({ data }) => ({
signature: holderKeys.authentication.sign(data),
keyType: holderKeys.authentication.type,
}),
)

console.log("✅ Asset transferred!");
}
main()
.then(() => console.log("\nBye! 👋 👋 👋 "))
.finally(Cord.disconnect);

process.on("SIGINT", async () => {
console.log("\nBye! 👋 👋 👋 \n");
Cord.disconnect();
process.exit(0);
});
14 changes: 7 additions & 7 deletions demo/src/utils/createAccount.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as Cord from '@cord.network/sdk'
import * as Cord from "@cord.network/sdk";

/**
* `createAccount` creates a new account from a mnemonic
Expand All @@ -7,17 +7,17 @@ import * as Cord from '@cord.network/sdk'
* @returns An object with two properties: account and mnemonic.
*/
export function createAccount(
mnemonic = Cord.Utils.Crypto.mnemonicGenerate()
mnemonic = Cord.Utils.Crypto.mnemonicGenerate(24)
): {
account: Cord.CordKeyringPair
mnemonic: string
account: Cord.CordKeyringPair;
mnemonic: string;
} {
const keyring = new Cord.Utils.Keyring({
ss58Format: 29,
type: 'ed25519',
})
type: "ed25519",
});
return {
account: keyring.addFromMnemonic(mnemonic) as Cord.CordKeyringPair,
mnemonic,
}
};
}
50 changes: 14 additions & 36 deletions demo/src/utils/createAuthorities.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as Cord from '@cord.network/sdk'
import { BN } from '@polkadot/util'
import { setTimeout } from 'timers/promises'
import * as Cord from "@cord.network/sdk";
import { BN } from "@polkadot/util";
import { setTimeout } from "timers/promises";

/**
* It tries to submit a transaction, and if it fails, it waits a bit and tries again
Expand All @@ -12,43 +12,21 @@ async function failproofSubmit(
submitter: Cord.KeyringPair
) {
try {
await Cord.Chain.signAndSubmitTx(tx, submitter)
await Cord.Chain.signAndSubmitTx(tx, submitter);
} catch {
// Try a second time after a small delay and fetching the right nonce.
const waitingTime = 6_000 // 6 seconds
const waitingTime = 6_000; // 6 seconds
console.log(
`First submission failed. Waiting ${waitingTime} ms before retrying.`
)
await setTimeout(waitingTime)
console.log('Retrying...')
);
await setTimeout(waitingTime);
console.log("Retrying...");
// nonce: -1 tells the client to fetch the latest nonce by also checking the tx pool.
const resignedBatchTx = await tx.signAsync(submitter, { nonce: -1 })
await Cord.Chain.submitSignedTx(resignedBatchTx)
const resignedBatchTx = await tx.signAsync(submitter, { nonce: -1 });
await Cord.Chain.submitSignedTx(resignedBatchTx);
}
}

/**
* It sends a transaction to the chain to transfer the specified amount of credits from the faucet
* account to the recipient account
* @param faucetAccount - The account that will be used to send the credits to the recipient.
* @param recipient - The address of the account you want to send credits to.
* @param {number} chainAmount - The amount of credits to transfer to the recipient.
*/
export async function getChainCredits(
faucetAccount: Cord.KeyringPair,
recipient: Cord.CordAddress,
chainAmount: number
) {
const api = Cord.ConfigService.get('api')

const tx = api.tx.balances.transfer(
recipient,
Cord.BalanceUtils.convertToTxUnit(new BN(chainAmount), 0)
)

await failproofSubmit(tx, faucetAccount)
}

/**
* It adds an authority to the list of authorities that can submit extrinsics to the chain
* @param authorAccount - The account that will be used to sign the transaction.
Expand All @@ -58,11 +36,11 @@ export async function addNetworkMember(
authorAccount: Cord.KeyringPair,
authority: Cord.CordAddress
) {
const api = Cord.ConfigService.get('api')
const api = Cord.ConfigService.get("api");

const callTx = api.tx.networkMembership.nominate(authority, false)
const callTx = api.tx.networkMembership.nominate(authority, false);

const sudoTx = await api.tx.sudo.sudo(callTx)
const sudoTx = await api.tx.sudo.sudo(callTx);

await failproofSubmit(sudoTx, authorAccount)
await failproofSubmit(sudoTx, authorAccount);
}
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"bundle": "yarn workspace @cord.network/sdk run bundle",
"clean": "rimraf tests/dist && yarn workspaces foreach -Ap --exclude '{root-workspace}' run clean",
"clean:docs": "rimraf docs/api",
"func-test": "tsx --no-cache demo/src/func-test.ts",
"set:version": "yarn workspaces foreach -pt exec npm version --no-git-tag-version",
"prepublish": "yarn workspaces foreach -Ap --no-private exec cp -f ../../LICENSE .",
"publish": "yarn workspaces foreach -Apt --no-private npm publish",
Expand All @@ -22,7 +21,10 @@
"style:fix": "yarn style --write",
"test": "jest --coverage --group=unit --detectOpenHandles",
"test:ci": "yarn test --ci --forceExit",
"test:watch": "yarn test --watch"
"test:watch": "yarn test --watch",
"demo-statement": "tsx --no-cache demo/src/func-test.ts",
"demo-network-score": "tsx --no-cache demo/src/network-score-test.ts",
"demo-asset": "tsx --no-cache demo/src/asset-tx.ts"
},
"husky": {
"hooks": {
Expand Down
43 changes: 43 additions & 0 deletions packages/asset/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@cord.network/asset",
"version": "0.9.0-1beta5",
"description": "CORD Asset Management",
"main": "./lib/cjs/index.js",
"module": "./lib/esm/index.js",
"types": "./lib/cjs/index.d.ts",
"exports": {
".": {
"import": "./lib/esm/index.js",
"require": "./lib/cjs/index.js"
}
},
"files": [
"lib/**/*"
],
"scripts": {
"clean": "rimraf ./lib",
"build": "yarn clean && yarn build:ts",
"build:ts": "yarn build:cjs && yarn build:esm",
"build:cjs": "tsc --declaration -p tsconfig.build.json && echo '{\"type\":\"commonjs\"}' > ./lib/cjs/package.json",
"build:esm": "tsc --declaration -p tsconfig.esm.json && echo '{\"type\":\"module\"}' > ./lib/esm/package.json"
},
"repository": "github:dhiway/cord-js",
"engines": {
"node": ">=20.0"
},
"author": "Dhiway",
"bugs": "https://github.com/dhiway/cord.js/issues",
"homepage": "https://github.com/dhiway/cord.js#readme",
"devDependencies": {
"rimraf": "^3.0.2",
"typescript": "^5.3.0"
},
"dependencies": {
"@cord.network/config": "workspace:*",
"@cord.network/did": "workspace:*",
"@cord.network/identifier": "workspace:*",
"@cord.network/network": "workspace:*",
"@cord.network/types": "workspace:*",
"@cord.network/utils": "workspace:*"
}
}
Loading

0 comments on commit eb8ccf1

Please sign in to comment.