difftreelog
Merge pull request #682 from UniqueNetwork/tests/require-await
in: master
Add requireAwait rule to avoid flakiness in tests
25 files changed
tests/.eslintrc.jsondiffbeforeafterboth--- a/tests/.eslintrc.json
+++ b/tests/.eslintrc.json
@@ -42,6 +42,7 @@
"avoidEscape": true
}
],
+ "require-await": 2,
"semi": [
"error",
"always"
tests/src/addCollectionAdmin.test.tsdiffbeforeafterboth--- a/tests/src/addCollectionAdmin.test.ts
+++ b/tests/src/addCollectionAdmin.test.ts
@@ -56,10 +56,8 @@
const collection = await helper.collection.getData(collectionId);
expect(collection?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
- const changeAdminTxBob = async () => helper.collection.addAdmin(bob, collectionId, {Substrate: bob.address});
- const changeAdminTxCharlie = async () => helper.collection.addAdmin(bob, collectionId, {Substrate: charlie.address});
- await expect(changeAdminTxCharlie()).to.be.rejectedWith(/common\.NoPermission/);
- await expect(changeAdminTxBob()).to.be.rejectedWith(/common\.NoPermission/);
+ await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/);
+ await expect(helper.collection.addAdmin(bob, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.NoPermission/);
const adminListAfterAddAdmin = await helper.collection.getAdmins(collectionId);
expect(adminListAfterAddAdmin).to.be.not.deep.contains({Substrate: charlie.address});
@@ -75,8 +73,7 @@
const adminListAfterAddAdmin = await collection.getAdmins();
expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: bob.address});
- const changeAdminTxCharlie = async () => collection.addAdmin(bob, {Substrate: charlie.address});
- await expect(changeAdminTxCharlie()).to.be.rejectedWith(/common\.NoPermission/);
+ await expect(collection.addAdmin(bob, {Substrate: charlie.address})).to.be.rejectedWith(/common\.NoPermission/);
const adminListAfterAddNewAdmin = await collection.getAdmins();
expect(adminListAfterAddNewAdmin).to.be.deep.contains({Substrate: bob.address});
@@ -87,8 +84,7 @@
const [alice, bob] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
const collectionId = (1 << 32) - 1;
- const addAdminTx = async () => helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});
- await expect(addAdminTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
+ await expect(helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/);
// Verifying that nothing bad happened (network is live, new collections can be created, etc.)
await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
@@ -99,8 +95,7 @@
const collection = await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
await collection.burn(alice);
- const addAdminTx = async () => collection.addAdmin(alice, {Substrate: bob.address});
- await expect(addAdminTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
+ await expect(collection.addAdmin(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.CollectionNotFound/);
// Verifying that nothing bad happened (network is live, new collections can be created, etc.)
await helper.nft.mintCollection(alice, {name: 'Collection Name', description: 'Collection Description', tokenPrefix: 'COL'});
@@ -119,7 +114,6 @@
expect(adminListAfterAddAdmin).to.be.deep.contains({Substrate: accounts[i].address});
}
- const addExtraAdminTx = async () => collection.addAdmin(alice, {Substrate: accounts[chainAdminLimit].address});
- await expect(addExtraAdminTx()).to.be.rejectedWith(/common\.CollectionAdminCountExceeded/);
+ await expect(collection.addAdmin(alice, {Substrate: accounts[chainAdminLimit].address})).to.be.rejectedWith(/common\.CollectionAdminCountExceeded/);
});
});
tests/src/adminTransferAndBurn.test.tsdiffbeforeafterboth--- a/tests/src/adminTransferAndBurn.test.ts
+++ b/tests/src/adminTransferAndBurn.test.ts
@@ -36,8 +36,7 @@
expect(limits.ownerCanTransfer).to.be.true;
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
- const transferResult = async () => helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address});
- await expect(transferResult()).to.be.rejected;
+ await expect(helper.nft.transferToken(alice, collectionId, tokenId, {Substrate: charlie.address})).to.be.rejected;
await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address});
const newTokenOwner = await helper.nft.getTokenOwner(collectionId, tokenId);
@@ -52,9 +51,8 @@
expect(limits.ownerCanTransfer).to.be.true;
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
- const burnTxFailed = async () => helper.nft.burnToken(alice, collectionId, tokenId);
- await expect(burnTxFailed()).to.be.rejected;
+ await expect(helper.nft.burnToken(alice, collectionId, tokenId)).to.be.rejected;
await helper.nft.burnToken(bob, collectionId, tokenId);
const token = await helper.nft.getToken(collectionId, tokenId);
tests/src/allowLists.test.tsdiffbeforeafterboth--- a/tests/src/allowLists.test.ts
+++ b/tests/src/allowLists.test.ts
@@ -30,7 +30,7 @@
});
});
- describe('Positive', async () => {
+ describe('Positive', () => {
itSub('Owner can add address to allow list', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
// allow list does not need to be enabled to add someone in advance
@@ -58,7 +58,7 @@
});
});
- describe('Negative', async () => {
+ describe('Negative', () => {
itSub('Nobody can add address to allow list of non-existing collection', async ({helper}) => {
const collectionId = (1<<32) - 1;
await expect(helper.nft.addToAllowList(bob, collectionId, {Substrate: charlie.address}))
@@ -92,7 +92,7 @@
});
});
- describe('Positive', async () => {
+ describe('Positive', () => {
itSub('Owner can remove address from allow list', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await helper.nft.addToAllowList(alice, collectionId, {Substrate: bob.address});
@@ -128,7 +128,7 @@
});
});
- describe('Negative', async () => {
+ describe('Negative', () => {
itSub('Non-privileged user cannot remove address from allow list', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await helper.nft.addToAllowList(alice, collectionId, {Substrate: charlie.address});
@@ -168,7 +168,7 @@
});
});
- describe('Positive', async () => {
+ describe('Positive', () => {
itSub('If Public Access mode is set to AllowList, tokens can be transferred to a allowlisted address with transfer.', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
@@ -219,7 +219,7 @@
});
});
- describe('Negative', async () => {
+ describe('Negative', () => {
itSub('If Public Access mode is set to AllowList, tokens can\'t be transferred from a non-allowlisted address with transfer or transferFrom. Test1', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
@@ -303,7 +303,7 @@
{access: 'AllowList', mintMode: true},
];
- const testPermissionSuite = async (permissions: ICollectionPermissions) => {
+ const testPermissionSuite = (permissions: ICollectionPermissions) => {
const allowlistedMintingShouldFail = !permissions.mintMode!;
const appropriateRejectionMessage = permissions.mintMode! ? /common\.AddressNotInAllowlist/ : /common\.PublicMintingNotAllowed/;
@@ -323,8 +323,8 @@
);
- describe(`Public Access Mode = ${permissions.access}, Mint Mode = ${permissions.mintMode}`, async () => {
- describe('Positive', async () => {
+ describe(`Public Access Mode = ${permissions.access}, Mint Mode = ${permissions.mintMode}`, () => {
+ describe('Positive', () => {
itSub('With the condtions above, tokens can be created by owner', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {});
await collection.setPermissions(alice, permissions);
@@ -341,7 +341,7 @@
if (!allowlistedMintingShouldFail) allowlistedMintingTest();
});
- describe('Negative', async () => {
+ describe('Negative', () => {
itSub('With the condtions above, tokens can\'t be created by non-priviliged non-allow-listed address', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await collection.setPermissions(alice, permissions);
tests/src/approve.test.tsdiffbeforeafterboth--- a/tests/src/approve.test.ts
+++ b/tests/src/approve.test.ts
@@ -92,7 +92,7 @@
itSub('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
- const approveTokenTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
+ const approveTokenTx = () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
await expect(approveTokenTx()).to.be.rejected;
});
});
@@ -196,7 +196,7 @@
await helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address});
const owner = await helper.nft.getTokenOwner(collectionId, tokenId);
expect(owner.Substrate).to.be.equal(alice.address);
- const transferTokenFromTx = async () => helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address});
+ const transferTokenFromTx = () => helper.nft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address});
await expect(transferTokenFromTx()).to.be.rejected;
});
@@ -210,7 +210,7 @@
const after = await helper.ft.getBalance(collectionId, {Substrate: alice.address});
expect(after - before).to.be.equal(BigInt(1));
- const transferTokenFromTx = async () => helper.ft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n);
+ const transferTokenFromTx = () => helper.ft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 1n);
await expect(transferTokenFromTx()).to.be.rejected;
});
@@ -222,7 +222,7 @@
await helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 100n);
const after = await helper.rft.getTokenBalance(collectionId, tokenId, {Substrate: alice.address});
expect(after - before).to.be.equal(BigInt(100));
- const transferTokenFromTx = async () => helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 100n);
+ const transferTokenFromTx = () => helper.rft.transferTokenFrom(charlie, collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address}, 100n);
await expect(transferTokenFromTx()).to.be.rejected;
});
});
@@ -277,7 +277,7 @@
expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.true;
await helper.signTransaction(alice, helper.constructApiCall('api.tx.unique.approve', [{Substrate: bob.address}, collectionId, tokenId, 0]));
expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: bob.address})).to.be.false;
- const transferTokenFromTx = async () => helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: bob.address});
+ const transferTokenFromTx = () => helper.nft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: bob.address});
await expect(transferTokenFromTx()).to.be.rejected;
});
@@ -293,7 +293,7 @@
const amountAfter = await helper.ft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address});
expect(amountAfter).to.be.equal(BigInt(0));
- const transferTokenFromTx = async () => helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}, 1n);
+ const transferTokenFromTx = () => helper.ft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}, 1n);
await expect(transferTokenFromTx()).to.be.rejected;
});
@@ -308,7 +308,7 @@
const amountAfter = await helper.rft.getTokenApprovedPieces(collectionId, tokenId, {Substrate: bob.address}, {Substrate: alice.address});
expect(amountAfter).to.be.equal(BigInt(0));
- const transferTokenFromTx = async () => helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}, 100n);
+ const transferTokenFromTx = () => helper.rft.transferTokenFrom(bob, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address}, 100n);
await expect(transferTokenFromTx()).to.be.rejected;
});
});
@@ -328,7 +328,7 @@
itSub('1 for NFT', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
- const approveTx = async () => helper.signTransaction(bob, helper.constructApiCall('api.tx.unique.approve', [{Substrate: charlie.address}, collectionId, tokenId, 2]));
+ const approveTx = () => helper.signTransaction(bob, helper.constructApiCall('api.tx.unique.approve', [{Substrate: charlie.address}, collectionId, tokenId, 2]));
await expect(approveTx()).to.be.rejected;
expect(await helper.nft.isTokenApproved(collectionId, tokenId, {Substrate: charlie.address})).to.be.false;
});
@@ -337,14 +337,14 @@
const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
await helper.ft.mintTokens(alice, collectionId, 10n, alice.address);
const tokenId = await helper.ft.getLastTokenId(collectionId);
- const approveTx = async () => helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 11n);
+ const approveTx = () => helper.ft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 11n);
await expect(approveTx()).to.be.rejected;
});
itSub.ifWithPallets('ReFungible', [Pallets.ReFungible], async ({helper}) => {
const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n});
- const approveTx = async () => helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 101n);
+ const approveTx = () => helper.rft.approveToken(alice, collectionId, tokenId, {Substrate: bob.address}, 101n);
await expect(approveTx()).to.be.rejected;
});
});
@@ -483,7 +483,7 @@
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
await helper.collection.addAdmin(alice, collectionId, {Substrate: bob.address});
- const approveTx = async () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address});
+ const approveTx = () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: charlie.address});
await expect(approveTx()).to.be.rejected;
});
});
@@ -502,59 +502,59 @@
itSub('[nft] Approve for a collection that does not exist', async ({helper}) => {
const collectionId = 1 << 32 - 1;
- const approveTx = async () => helper.nft.approveToken(bob, collectionId, 1, {Substrate: charlie.address});
+ const approveTx = () => helper.nft.approveToken(bob, collectionId, 1, {Substrate: charlie.address});
await expect(approveTx()).to.be.rejected;
});
itSub('[fungible] Approve for a collection that does not exist', async ({helper}) => {
const collectionId = 1 << 32 - 1;
- const approveTx = async () => helper.ft.approveToken(bob, collectionId, 1, {Substrate: charlie.address});
+ const approveTx = () => helper.ft.approveToken(bob, collectionId, 1, {Substrate: charlie.address});
await expect(approveTx()).to.be.rejected;
});
itSub.ifWithPallets('[refungible] Approve for a collection that does not exist', [Pallets.ReFungible], async ({helper}) => {
const collectionId = 1 << 32 - 1;
- const approveTx = async () => helper.rft.approveToken(bob, collectionId, 1, {Substrate: charlie.address});
+ const approveTx = () => helper.rft.approveToken(bob, collectionId, 1, {Substrate: charlie.address});
await expect(approveTx()).to.be.rejected;
});
itSub('[nft] Approve for a collection that was destroyed', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await helper.nft.burn(alice, collectionId);
- const approveTx = async () => helper.nft.approveToken(alice, collectionId, 1, {Substrate: bob.address});
+ const approveTx = () => helper.nft.approveToken(alice, collectionId, 1, {Substrate: bob.address});
await expect(approveTx()).to.be.rejected;
});
itSub('[fungible] Approve for a collection that was destroyed', async ({helper}) => {
const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await helper.ft.burn(alice, collectionId);
- const approveTx = async () => helper.ft.approveToken(alice, collectionId, 1, {Substrate: bob.address});
+ const approveTx = () => helper.ft.approveToken(alice, collectionId, 1, {Substrate: bob.address});
await expect(approveTx()).to.be.rejected;
});
itSub.ifWithPallets('[refungible] Approve for a collection that was destroyed', [Pallets.ReFungible], async ({helper}) => {
const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await helper.rft.burn(alice, collectionId);
- const approveTx = async () => helper.rft.approveToken(alice, collectionId, 1, {Substrate: bob.address});
+ const approveTx = () => helper.rft.approveToken(alice, collectionId, 1, {Substrate: bob.address});
await expect(approveTx()).to.be.rejected;
});
itSub('[nft] Approve transfer of a token that does not exist', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const approveTx = async () => helper.nft.approveToken(alice, collectionId, 2, {Substrate: bob.address});
+ const approveTx = () => helper.nft.approveToken(alice, collectionId, 2, {Substrate: bob.address});
await expect(approveTx()).to.be.rejected;
});
itSub.ifWithPallets('[refungible] Approve transfer of a token that does not exist', [Pallets.ReFungible], async ({helper}) => {
const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const approveTx = async () => helper.rft.approveToken(alice, collectionId, 2, {Substrate: bob.address});
+ const approveTx = () => helper.rft.approveToken(alice, collectionId, 2, {Substrate: bob.address});
await expect(approveTx()).to.be.rejected;
});
itSub('[nft] Approve using the address that does not own the approved token', async ({helper}) => {
const {collectionId} = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: alice.address});
- const approveTx = async () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address});
+ const approveTx = () => helper.nft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address});
await expect(approveTx()).to.be.rejected;
});
@@ -562,14 +562,14 @@
const {collectionId} = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await helper.ft.mintTokens(alice, collectionId, 10n, alice.address);
const tokenId = await helper.ft.getLastTokenId(collectionId);
- const approveTx = async () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address});
+ const approveTx = () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address});
await expect(approveTx()).to.be.rejected;
});
itSub.ifWithPallets('[refungible] Approve using the address that does not own the approved token', [Pallets.ReFungible], async ({helper}) => {
const {collectionId} = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
const {tokenId} = await helper.rft.mintToken(alice, {collectionId: collectionId, owner: alice.address, pieces: 100n});
- const approveTx = async () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address});
+ const approveTx = () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address});
await expect(approveTx()).to.be.rejected;
});
@@ -579,7 +579,7 @@
await helper.rft.transferToken(alice, collectionId, tokenId, {Substrate: bob.address}, 100n);
await helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 100n);
- const approveTx = async () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 101n);
+ const approveTx = () => helper.rft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 101n);
await expect(approveTx()).to.be.rejected;
});
@@ -590,7 +590,7 @@
await helper.ft.transferToken(alice, collectionId, tokenId, {Substrate: bob.address}, 10n);
await helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 10n);
- const approveTx = async () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 11n);
+ const approveTx = () => helper.ft.approveToken(bob, collectionId, tokenId, {Substrate: alice.address}, 11n);
await expect(approveTx()).to.be.rejected;
});
@@ -599,7 +599,7 @@
const {tokenId} = await helper.nft.mintToken(alice, {collectionId: collectionId, owner: bob.address});
await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: false});
- const approveTx = async () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
+ const approveTx = () => helper.nft.approveToken(alice, collectionId, tokenId, {Substrate: charlie.address});
await expect(approveTx()).to.be.rejected;
});
});
tests/src/change-collection-owner.test.tsdiffbeforeafterboth--- a/tests/src/change-collection-owner.test.ts
+++ b/tests/src/change-collection-owner.test.ts
@@ -56,7 +56,7 @@
await collection.changeOwner(alice, bob.address);
- const changeOwnerTx = async () => collection.changeOwner(alice, alice.address);
+ const changeOwnerTx = () => collection.changeOwner(alice, alice.address);
await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
const afterChanging = await helper.collection.getData(collection.collectionId);
@@ -115,20 +115,20 @@
itSub('Not owner can\'t change owner.', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const changeOwnerTx = async () => collection.changeOwner(bob, bob.address);
+ const changeOwnerTx = () => collection.changeOwner(bob, bob.address);
await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
});
itSub('Collection admin can\'t change owner.', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await collection.addAdmin(alice, {Substrate: bob.address});
- const changeOwnerTx = async () => collection.changeOwner(bob, bob.address);
+ const changeOwnerTx = () => collection.changeOwner(bob, bob.address);
await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
});
itSub('Can\'t change owner of a non-existing collection.', async ({helper}) => {
const collectionId = (1 << 32) - 1;
- const changeOwnerTx = async () => helper.collection.changeOwner(bob, collectionId, bob.address);
+ const changeOwnerTx = () => helper.collection.changeOwner(bob, collectionId, bob.address);
await expect(changeOwnerTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
});
@@ -136,15 +136,15 @@
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await collection.changeOwner(alice, bob.address);
- const changeOwnerTx = async () => collection.changeOwner(alice, alice.address);
+ const changeOwnerTx = () => collection.changeOwner(alice, alice.address);
await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
const afterChanging = await helper.collection.getData(collection.collectionId);
expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
- const setSponsorTx = async () => collection.setSponsor(alice, charlie.address);
- const confirmSponsorshipTx = async () => collection.confirmSponsorship(alice);
- const removeSponsorTx = async () => collection.removeSponsor(alice);
+ const setSponsorTx = () => collection.setSponsor(alice, charlie.address);
+ const confirmSponsorshipTx = () => collection.confirmSponsorship(alice);
+ const removeSponsorTx = () => collection.removeSponsor(alice);
await expect(setSponsorTx()).to.be.rejectedWith(/common\.NoPermission/);
await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
await expect(removeSponsorTx()).to.be.rejectedWith(/common\.NoPermission/);
@@ -157,13 +157,13 @@
ownerCanTransfer: true,
};
- const setLimitsTx = async () => collection.setLimits(alice, limits);
+ const setLimitsTx = () => collection.setLimits(alice, limits);
await expect(setLimitsTx()).to.be.rejectedWith(/common\.NoPermission/);
- const setPermissionTx = async () => collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
+ const setPermissionTx = () => collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
await expect(setPermissionTx()).to.be.rejectedWith(/common\.NoPermission/);
- const burnTx = async () => collection.burn(alice);
+ const burnTx = () => collection.burn(alice);
await expect(burnTx()).to.be.rejectedWith(/common\.NoPermission/);
});
});
tests/src/confirmSponsorship.test.tsdiffbeforeafterboth--- a/tests/src/confirmSponsorship.test.ts
+++ b/tests/src/confirmSponsorship.test.ts
@@ -119,7 +119,7 @@
await token.transfer(alice, {Substrate: zeroBalance.address});
const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
- const transferTx = async () => token.transfer(zeroBalance, {Substrate: alice.address});
+ const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address});
await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
@@ -137,7 +137,7 @@
await collection.transfer(zeroBalance, {Substrate: zeroBalance.address}, 1n);
const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
- const transferTx = async () => collection.transfer(zeroBalance, {Substrate: zeroBalance.address});
+ const transferTx = () => collection.transfer(zeroBalance, {Substrate: zeroBalance.address});
await expect(transferTx()).to.be.rejected;
const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
@@ -155,7 +155,7 @@
await token.transfer(zeroBalance, {Substrate: alice.address});
const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
- const transferTx = async () => token.transfer(zeroBalance, {Substrate: alice.address});
+ const transferTx = () => token.transfer(zeroBalance, {Substrate: alice.address});
await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
@@ -175,7 +175,7 @@
await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
- const mintTx = async () => collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
+ const mintTx = () => collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
await expect(mintTx()).to.be.rejectedWith('Inability to pay some fees');
const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
@@ -199,21 +199,21 @@
itSub('(!negative test!) Confirm sponsorship for a collection that never existed', async ({helper}) => {
const collectionId = (1 << 32) - 1;
- const confirmSponsorshipTx = async () => helper.collection.confirmSponsorship(bob, collectionId);
+ const confirmSponsorshipTx = () => helper.collection.confirmSponsorship(bob, collectionId);
await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
});
itSub('(!negative test!) Confirm sponsorship using a non-sponsor address', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await collection.setSponsor(alice, bob.address);
- const confirmSponsorshipTx = async () => collection.confirmSponsorship(charlie);
+ const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
});
itSub('(!negative test!) Confirm sponsorship using owner address', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await collection.setSponsor(alice, bob.address);
- const confirmSponsorshipTx = async () => collection.confirmSponsorship(alice);
+ const confirmSponsorshipTx = () => collection.confirmSponsorship(alice);
await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
});
@@ -221,20 +221,20 @@
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await collection.setSponsor(alice, bob.address);
await collection.addAdmin(alice, {Substrate: charlie.address});
- const confirmSponsorshipTx = async () => collection.confirmSponsorship(charlie);
+ const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
});
itSub('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const confirmSponsorshipTx = async () => collection.confirmSponsorship(charlie);
+ const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
});
itSub('(!negative test!) Confirm sponsorship in a collection that was destroyed', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
await collection.burn(alice);
- const confirmSponsorshipTx = async () => collection.confirmSponsorship(charlie);
+ const confirmSponsorshipTx = () => collection.confirmSponsorship(charlie);
await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
});
@@ -244,7 +244,7 @@
await collection.confirmSponsorship(bob);
const token = await collection.mintToken(alice, {Substrate: ownerZeroBalance.address});
const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address);
- const transferTx = async () => token.transfer(senderZeroBalance, {Substrate: alice.address});
+ const transferTx = () => token.transfer(senderZeroBalance, {Substrate: alice.address});
await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address);
expect(sponsorBalanceAfter).to.equal(sponsorBalanceBefore);
tests/src/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/createCollection.test.ts
+++ b/tests/src/createCollection.test.ts
@@ -116,20 +116,20 @@
});
itSub('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async ({helper}) => {
- const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'A'.repeat(65), description: 'descr', tokenPrefix: 'COL'});
+ const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'A'.repeat(65), description: 'descr', tokenPrefix: 'COL'});
await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
});
itSub('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async ({helper}) => {
- const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'A'.repeat(257), tokenPrefix: 'COL'});
+ const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'A'.repeat(257), tokenPrefix: 'COL'});
await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
});
itSub('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async ({helper}) => {
- const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(17)});
+ const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(17)});
await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
});
itSub('(!negative test!) fails when bad limits are set', async ({helper}) => {
- const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', limits: {tokenLimit: 0}});
+ const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', limits: {tokenLimit: 0}});
await expect(mintCollectionTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
});
@@ -139,7 +139,7 @@
for (let i = 0; i < 65; i++) {
props.push({key: `key${i}`, value: `value${i}`});
}
- const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
+ const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
});
@@ -150,7 +150,7 @@
props.push({key: `key${i}`.repeat(80), value: `value${i}`.repeat(80)});
}
- const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
+ const mintCollectionTx = () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
});
});
tests/src/createItem.test.tsdiffbeforeafterboth--- a/tests/src/createItem.test.ts
+++ b/tests/src/createItem.test.ts
@@ -186,17 +186,17 @@
itSub('Regular user cannot create new item in NFT collection', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const mintTx = async () => collection.mintToken(bob, {Substrate: bob.address});
+ const mintTx = () => collection.mintToken(bob, {Substrate: bob.address});
await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
});
itSub('Regular user cannot create new item in Fungible collection', async ({helper}) => {
const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
- const mintTx = async () => collection.mint(bob, 10n, {Substrate: bob.address});
+ const mintTx = () => collection.mint(bob, 10n, {Substrate: bob.address});
await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
});
itSub.ifWithPallets('Regular user cannot create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const mintTx = async () => collection.mintToken(bob, 100n, {Substrate: bob.address});
+ const mintTx = () => collection.mintToken(bob, 100n, {Substrate: bob.address});
await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
});
@@ -204,7 +204,7 @@
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
tokenPropertyPermissions: [{key: 'k', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}],
});
- const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
+ const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
});
@@ -212,13 +212,13 @@
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
tokenPropertyPermissions: [{key: 'k', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}],
});
- const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
+ const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
});
itSub('Adding property without access rights', async ({helper}) => {
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
+ const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
});
@@ -231,7 +231,7 @@
const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
- const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, props);
+ const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, props);
await expect(mintTx()).to.be.rejectedWith('Verification Error');
});
@@ -242,7 +242,7 @@
{key: 'k2', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
],
});
- const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, [
+ const mintTx = () => collection.mintToken(alice, {Substrate: bob.address}, [
{key: 'k1', value: 'vvvvvv'.repeat(5000)},
{key: 'k2', value: 'vvv'.repeat(5000)},
]);
tests/src/createMultipleItems.test.tsdiffbeforeafterboth--- a/tests/src/createMultipleItems.test.ts
+++ b/tests/src/createMultipleItems.test.ts
@@ -183,7 +183,7 @@
{},
{},
];
- const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
});
@@ -198,7 +198,7 @@
{value: 2n},
{value: 3n},
];
- const mintTx = async () => helper.ft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, args, {Substrate: alice.address});
+ const mintTx = () => helper.ft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, args, {Substrate: alice.address});
await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
});
@@ -213,7 +213,7 @@
{pieces: 1n},
{pieces: 1n},
];
- const mintTx = async () => helper.rft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
});
@@ -223,7 +223,7 @@
{},
{},
];
- const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(bob, collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(bob, collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
});
@@ -241,7 +241,7 @@
{properties: [{key: 'data', value: 'B'.repeat(32769)}]},
{properties: [{key: 'data', value: 'C'.repeat(32769)}]},
];
- const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith('Verification Error');
});
@@ -259,7 +259,7 @@
{pieces: 10n, properties: [{key: 'data', value: 'B'.repeat(32769)}]},
{pieces: 10n, properties: [{key: 'data', value: 'C'.repeat(32769)}]},
];
- const mintTx = async () => helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith('Verification Error');
});
@@ -291,7 +291,7 @@
{properties: [{key: 'data', value: 'A'}]},
{properties: [{key: 'data', value: 'B'.repeat(32769)}]},
];
- const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith('Verification Error');
});
@@ -311,7 +311,7 @@
{},
{},
];
- const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
});
@@ -327,7 +327,7 @@
const args = [
{properties: [{key: 'data', value: 'A'}]},
];
- const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
});
@@ -346,7 +346,7 @@
const args = [
{properties: [{key: 'data', value: 'A'}]},
];
- const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
});
@@ -368,7 +368,7 @@
{properties: prps},
];
- const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
+ const mintTx = () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
await expect(mintTx()).to.be.rejectedWith('Verification Error');
});
});
tests/src/eth/base.test.tsdiffbeforeafterboth--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -69,7 +69,7 @@
});
});
-describe('ERC165 tests', async () => {
+describe('ERC165 tests', () => {
// https://eips.ethereum.org/EIPS/eip-165
let erc721MetadataCompatibleNftCollectionId: number;
tests/src/eth/nesting/nest.test.tsdiffbeforeafterboth--- a/tests/src/eth/nesting/nest.test.ts
+++ b/tests/src/eth/nesting/nest.test.ts
@@ -81,7 +81,7 @@
});
});
- describe('Negative Test: EVM Nesting', async() => {
+ describe('Negative Test: EVM Nesting', () => {
itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
tests/src/eth/util/index.tsdiffbeforeafterboth--- a/tests/src/eth/util/index.ts
+++ b/tests/src/eth/util/index.ts
@@ -58,7 +58,7 @@
}
};
-export async function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+export function itEth(name: string, cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
(opts.only ? it.only :
opts.skip ? it.skip : it)(name, async function() {
await usingEthPlaygrounds(async (helper, privateKey) => {
@@ -71,7 +71,7 @@
});
}
-export async function itEthIfWithPallet(name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+export function itEthIfWithPallet(name: string, required: string[], cb: (apis: { helper: EthUniqueHelper, privateKey: (seed: string | {filename: string}) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
return itEth(name, cb, {requiredPallets: required, ...opts});
}
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -188,7 +188,7 @@
return {collectionId, collectionAddress, events};
}
- async createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
+ createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {
return this.createCollecion('createNFTCollection', signer, name, description, tokenPrefix);
}
@@ -202,7 +202,7 @@
return {collectionId, collectionAddress, events};
}
- async createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string, events: NormalizedEvent[]}> {
+ createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string, events: NormalizedEvent[]}> {
return this.createCollecion('createRFTCollection', signer, name, description, tokenPrefix);
}
@@ -409,7 +409,7 @@
return this.web3;
}
- async connectWeb3(wsEndpoint: string) {
+ connectWeb3(wsEndpoint: string) {
if(this.web3 !== null) return;
this.web3Provider = new Web3.providers.WebsocketProvider(wsEndpoint);
this.web3 = new Web3(this.web3Provider);
tests/src/pallet-presence.test.tsdiffbeforeafterboth--- a/tests/src/pallet-presence.test.ts
+++ b/tests/src/pallet-presence.test.ts
@@ -86,15 +86,15 @@
});
});
- itSub('Required pallets are present', async ({helper}) => {
+ itSub('Required pallets are present', ({helper}) => {
expect(helper.fetchAllPalletNames()).to.contain.members([...requiredPallets]);
});
- itSub('Governance and consensus pallets are present', async ({helper}) => {
+ itSub('Governance and consensus pallets are present', ({helper}) => {
expect(helper.fetchAllPalletNames()).to.contain.members([...consensusPallets]);
});
- itSub('No extra pallets are included', async ({helper}) => {
+ itSub('No extra pallets are included', ({helper}) => {
expect(helper.fetchAllPalletNames().sort()).to.be.deep.equal([...requiredPallets, ...consensusPallets].sort());
});
});
tests/src/refungible.test.tsdiffbeforeafterboth--- a/tests/src/refungible.test.ts
+++ b/tests/src/refungible.test.ts
@@ -19,7 +19,7 @@
const MAX_REFUNGIBLE_PIECES = 1_000_000_000_000_000_000_000n;
-describe('integration test: Refungible functionality:', async () => {
+describe('integration test: Refungible functionality:', () => {
let donor: IKeyringPair;
let alice: IKeyringPair;
let bob: IKeyringPair;
tests/src/rmrk/addResource.seqtest.tsdiffbeforeafterboth--- a/tests/src/rmrk/addResource.seqtest.ts
+++ b/tests/src/rmrk/addResource.seqtest.ts
@@ -112,7 +112,7 @@
const resourcesInfo = [];
const resourceNum = 4;
- const checkResource = async (resource: ResourceInfo, resType: string, expectedId: number, expected: {
+ const checkResource = (resource: ResourceInfo, resType: string, expectedId: number, expected: {
src: string,
metadata: string,
license: string,
tests/src/rmrk/equipNft.seqtest.tsdiffbeforeafterboth--- a/tests/src/rmrk/equipNft.seqtest.ts
+++ b/tests/src/rmrk/equipNft.seqtest.ts
@@ -30,7 +30,7 @@
const slotId = 1;
-async function createTestCollection(api: ApiPromise): Promise<number> {
+function createTestCollection(api: ApiPromise): Promise<number> {
return createCollection(
api,
alice,
@@ -60,7 +60,7 @@
return nftChildId;
}
-async function createTestBase(api: ApiPromise): Promise<number> {
+function createTestBase(api: ApiPromise): Promise<number> {
return createBase(api, alice, 'test-base', 'DTBase', [
{
SlotPart: {
tests/src/rmrk/rmrkIsolation.seqtest.tsdiffbeforeafterboth--- a/tests/src/rmrk/rmrkIsolation.seqtest.ts
+++ b/tests/src/rmrk/rmrkIsolation.seqtest.ts
@@ -58,7 +58,7 @@
return rmrkNftId;
}
-describe('RMRK External Integration Test', async () => {
+describe('RMRK External Integration Test', () => {
before(async function() {
await usingPlaygrounds(async (_helper, privateKey) => {
alice = await privateKey('//Alice');
@@ -78,7 +78,7 @@
});
});
-describe('Negative Integration Test: External Collections, Internal Ops', async () => {
+describe('Negative Integration Test: External Collections, Internal Ops', () => {
let uniqueCollectionId: number;
let rmrkCollectionId: number;
let rmrkNftId: number;
@@ -196,7 +196,7 @@
});
});
-describe('Negative Integration Test: Internal Collections, External Ops', async () => {
+describe('Negative Integration Test: Internal Collections, External Ops', () => {
let collectionId: number;
let nftId: number;
tests/src/rmrk/util/fetch.tsdiffbeforeafterboth--- a/tests/src/rmrk/util/fetch.ts
+++ b/tests/src/rmrk/util/fetch.ts
@@ -20,7 +20,7 @@
return (await api.rpc.rmrk.lastCollectionIdx()).toNumber();
}
-export async function getCollection(api: ApiPromise, id: number): Promise<Option<Collection>> {
+export function getCollection(api: ApiPromise, id: number): Promise<Option<Collection>> {
return api.rpc.rmrk.collectionById(id);
}
@@ -36,7 +36,7 @@
.map((value) => value.toNumber());
}
-export async function getNft(api: ApiPromise, collectionId: number, nftId: number): Promise<Option<Nft>> {
+export function getNft(api: ApiPromise, collectionId: number, nftId: number): Promise<Option<Nft>> {
return api.rpc.rmrk.nftById(collectionId, nftId);
}
@@ -56,7 +56,7 @@
return (await api.rpc.rmrk.nftChildren(collectionId, nftId)).toArray();
}
-export async function getBase(api: ApiPromise, baseId: number): Promise<Option<Base>> {
+export function getBase(api: ApiPromise, baseId: number): Promise<Option<Base>> {
return api.rpc.rmrk.base(baseId);
}
@@ -112,7 +112,7 @@
.map((name) => name.toUtf8());
}
-export async function getTheme(
+export function getTheme(
api: ApiPromise,
baseId: number,
themeName: string,
tests/src/tx-version-presence.test.tsdiffbeforeafterboth--- a/tests/src/tx-version-presence.test.ts
+++ b/tests/src/tx-version-presence.test.ts
@@ -26,7 +26,7 @@
});
});
- itSub('Signed extension CheckTxVersion is present', async () => {
+ itSub('Signed extension CheckTxVersion is present', () => {
expect(metadata.asLatest.extrinsic.signedExtensions.map(se => se.identifier.toString())).to.include('CheckTxVersion');
});
});
tests/src/util/globalSetup.tsdiffbeforeafterboth--- a/tests/src/util/globalSetup.ts
+++ b/tests/src/util/globalSetup.ts
@@ -90,7 +90,7 @@
});
};
-const fundFilenamesWithRetries = async (retriesLeft: number): Promise<boolean> => {
+const fundFilenamesWithRetries = (retriesLeft: number): Promise<boolean> => {
if (retriesLeft <= 0) return Promise.resolve(false);
return fundFilenames()
.then(() => Promise.resolve(true))
tests/src/util/index.tsdiffbeforeafterboth--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -59,27 +59,27 @@
return usingPlaygroundsGeneral<DevUniqueHelper>(DevUniqueHelper, url, code);
};
-export const usingWestmintPlaygrounds = async (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+export const usingWestmintPlaygrounds = (url: string, code: (helper: DevWestmintHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
return usingPlaygroundsGeneral<DevWestmintHelper>(DevWestmintHelper, url, code);
};
-export const usingRelayPlaygrounds = async (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+export const usingRelayPlaygrounds = (url: string, code: (helper: DevRelayHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
return usingPlaygroundsGeneral<DevRelayHelper>(DevRelayHelper, url, code);
};
-export const usingAcalaPlaygrounds = async (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+export const usingAcalaPlaygrounds = (url: string, code: (helper: DevAcalaHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
return usingPlaygroundsGeneral<DevAcalaHelper>(DevAcalaHelper, url, code);
};
-export const usingKaruraPlaygrounds = async (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+export const usingKaruraPlaygrounds = (url: string, code: (helper: DevKaruraHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
return usingPlaygroundsGeneral<DevKaruraHelper>(DevAcalaHelper, url, code);
};
-export const usingMoonbeamPlaygrounds = async (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+export const usingMoonbeamPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
return usingPlaygroundsGeneral<DevMoonbeamHelper>(DevMoonbeamHelper, url, code);
};
-export const usingMoonriverPlaygrounds = async (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
+export const usingMoonriverPlaygrounds = (url: string, code: (helper: DevMoonbeamHelper, privateKey: (seed: string) => Promise<IKeyringPair>) => Promise<void>) => {
return usingPlaygroundsGeneral<DevMoonriverHelper>(DevMoonriverHelper, url, code);
};
@@ -108,7 +108,7 @@
}
}
-export async function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+export function itSub(name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
(opts.only ? it.only :
opts.skip ? it.skip : it)(name, async function () {
await usingPlaygrounds(async (helper, privateKey) => {
@@ -120,7 +120,7 @@
});
});
}
-export async function itSubIfWithPallet(name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
+export function itSubIfWithPallet(name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any, opts: { only?: boolean, skip?: boolean, requiredPallets?: string[] } = {}) {
return itSub(name, cb, {requiredPallets: required, ...opts});
}
itSub.only = (name: string, cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSub(name, cb, {only: true});
@@ -130,7 +130,7 @@
itSubIfWithPallet.skip = (name: string, required: string[], cb: (apis: { helper: DevUniqueHelper, privateKey: (seed: string) => Promise<IKeyringPair> }) => any) => itSubIfWithPallet(name, required, cb, {skip: true});
itSub.ifWithPallets = itSubIfWithPallet;
-export async function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {
+export function describeXCM(title: string, fn: (this: Mocha.Suite) => void, opts: {skip?: boolean} = {}) {
(process.env.RUN_XCM_TESTS && !opts.skip
? describe
: describe.skip)(title, fn);
tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -273,7 +273,7 @@
}
const block2 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])]);
const block1 = await this.helper.callRpc('api.rpc.chain.getBlock', [await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber - 1])]);
- const findCreationDate = async (block: any) => {
+ const findCreationDate = (block: any) => {
const humanBlock = block.toHuman();
let date;
humanBlock.block.extrinsics.forEach((ext: any) => {
@@ -388,7 +388,7 @@
* @param blocksCount number of blocks to wait
* @returns
*/
- async newBlocks(blocksCount = 1): Promise<void> {
+ newBlocks(blocksCount = 1): Promise<void> {
// eslint-disable-next-line no-async-promise-executor
const promise = new Promise<void>(async (resolve) => {
const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(() => {
@@ -403,10 +403,10 @@
return promise;
}
- async forParachainBlockNumber(blockNumber: bigint) {
+ forParachainBlockNumber(blockNumber: bigint) {
// eslint-disable-next-line no-async-promise-executor
return new Promise<void>(async (resolve) => {
- const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async (data: any) => {
+ const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads((data: any) => {
if (data.number.toNumber() >= blockNumber) {
unsubscribe();
resolve();
@@ -415,10 +415,10 @@
});
}
- async forRelayBlockNumber(blockNumber: bigint) {
+ forRelayBlockNumber(blockNumber: bigint) {
// eslint-disable-next-line no-async-promise-executor
return new Promise<void>(async (resolve) => {
- const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData(async (data: any) => {
+ const unsubscribe = await this.helper.getApi().query.parachainSystem.validationData((data: any) => {
if (data.value.relayParentNumber.toNumber() >= blockNumber) {
// @ts-ignore
unsubscribe();
@@ -428,7 +428,7 @@
});
}
- async noScheduledTasks() {
+ noScheduledTasks() {
const api = this.helper.getApi();
// eslint-disable-next-line no-async-promise-executor
@@ -446,7 +446,7 @@
return promise;
}
- async event(maxBlocksToWait: number, eventSection: string, eventMethod: string) {
+ event(maxBlocksToWait: number, eventSection: string, eventMethod: string) {
// eslint-disable-next-line no-async-promise-executor
const promise = new Promise<EventRecord | null>(async (resolve) => {
const unsubscribe = await this.helper.getApi().rpc.chain.subscribeNewHeads(async header => {
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034/* eslint-disable @typescript-eslint/no-var-requires */5/* eslint-disable function-call-argument-newline */6/* eslint-disable no-prototype-builtins */78import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';12import {13 IApiListeners,14 IBlock,15 IEvent,16 IChainProperties,17 ICollectionCreationOptions,18 ICollectionLimits,19 ICollectionPermissions,20 ICrossAccountId,21 ICrossAccountIdLower,22 ILogger,23 INestingPermissions,24 IProperty,25 IStakingInfo,26 ISchedulerOptions,27 ISubstrateBalance,28 IToken,29 ITokenPropertyPermission,30 ITransactionResult,31 IUniqueHelperLog,32 TApiAllowedListeners,33 TEthereumAccount,34 TSigner,35 TSubstrateAccount,36 TNetworks,37 IForeignAssetMetadata,38 AcalaAssetMetadata,39 MoonbeamAssetInfo,40 DemocracyStandardAccountVote,41} from './types';42import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';4344export class CrossAccountId implements ICrossAccountId {45 Substrate?: TSubstrateAccount;46 Ethereum?: TEthereumAccount;4748 constructor(account: ICrossAccountId) {49 if (account.Substrate) this.Substrate = account.Substrate;50 if (account.Ethereum) this.Ethereum = account.Ethereum;51 }5253 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {54 switch (domain) {55 case 'Substrate': return new CrossAccountId({Substrate: account.address});56 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();57 }58 }5960 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {61 return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});62 }6364 static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {65 return encodeAddress(decodeAddress(address), ss58Format);66 }6768 static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {69 return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});70 }7172 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {73 if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);74 return this;75 }7677 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {78 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));79 }8081 toEthereum(): CrossAccountId {82 if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});83 return this;84 }8586 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {87 return evmToAddress(address, ss58Format);88 }8990 toSubstrate(ss58Format?: number): CrossAccountId {91 if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});92 return this;93 }9495 toLowerCase(): CrossAccountId {96 if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();97 if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();98 return this;99 }100}101102const nesting = {103 toChecksumAddress(address: string): string {104 if (typeof address === 'undefined') return '';105106 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);107108 address = address.toLowerCase().replace(/^0x/i,'');109 const addressHash = keccakAsHex(address).replace(/^0x/i,'');110 const checksumAddress = ['0x'];111112 for (let i = 0; i < address.length; i++) {113 // If ith character is 8 to f then make it uppercase114 if (parseInt(addressHash[i], 16) > 7) {115 checksumAddress.push(address[i].toUpperCase());116 } else {117 checksumAddress.push(address[i]);118 }119 }120 return checksumAddress.join('');121 },122 tokenIdToAddress(collectionId: number, tokenId: number) {123 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8,'0')}${tokenId.toString(16).padStart(8,'0')}`);124 },125};126127class UniqueUtil {128 static transactionStatus = {129 NOT_READY: 'NotReady',130 FAIL: 'Fail',131 SUCCESS: 'Success',132 };133134 static chainLogType = {135 EXTRINSIC: 'extrinsic',136 RPC: 'rpc',137 };138139 static getTokenAccount(token: IToken): CrossAccountId {140 return new CrossAccountId({Ethereum: this.getTokenAddress(token)});141 }142143 static getTokenAddress(token: IToken): string {144 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);145 }146147 static getDefaultLogger(): ILogger {148 return {149 log(msg: any, level = 'INFO') {150 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));151 },152 level: {153 ERROR: 'ERROR',154 WARNING: 'WARNING',155 INFO: 'INFO',156 },157 };158 }159160 static vec2str(arr: string[] | number[]) {161 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');162 }163164 static str2vec(string: string) {165 if (typeof string !== 'string') return string;166 return Array.from(string).map(x => x.charCodeAt(0));167 }168169 static fromSeed(seed: string, ss58Format = 42) {170 const keyring = new Keyring({type: 'sr25519', ss58Format});171 return keyring.addFromUri(seed);172 }173174 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {175 if (creationResult.status !== this.transactionStatus.SUCCESS) {176 throw Error('Unable to create collection!');177 }178179 let collectionId = null;180 creationResult.result.events.forEach(({event: {data, method, section}}) => {181 if ((section === 'common') && (method === 'CollectionCreated')) {182 collectionId = parseInt(data[0].toString(), 10);183 }184 });185186 if (collectionId === null) {187 throw Error('No CollectionCreated event was found!');188 }189190 return collectionId;191 }192193 static extractTokensFromCreationResult(creationResult: ITransactionResult): {194 success: boolean,195 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],196 } {197 if (creationResult.status !== this.transactionStatus.SUCCESS) {198 throw Error('Unable to create tokens!');199 }200 let success = false;201 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];202 creationResult.result.events.forEach(({event: {data, method, section}}) => {203 if (method === 'ExtrinsicSuccess') {204 success = true;205 } else if ((section === 'common') && (method === 'ItemCreated')) {206 tokens.push({207 collectionId: parseInt(data[0].toString(), 10),208 tokenId: parseInt(data[1].toString(), 10),209 owner: data[2].toHuman(),210 amount: data[3].toBigInt(),211 });212 }213 });214 return {success, tokens};215 }216217 static extractTokensFromBurnResult(burnResult: ITransactionResult): {218 success: boolean,219 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],220 } {221 if (burnResult.status !== this.transactionStatus.SUCCESS) {222 throw Error('Unable to burn tokens!');223 }224 let success = false;225 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];226 burnResult.result.events.forEach(({event: {data, method, section}}) => {227 if (method === 'ExtrinsicSuccess') {228 success = true;229 } else if ((section === 'common') && (method === 'ItemDestroyed')) {230 tokens.push({231 collectionId: parseInt(data[0].toString(), 10),232 tokenId: parseInt(data[1].toString(), 10),233 owner: data[2].toHuman(),234 amount: data[3].toBigInt(),235 });236 }237 });238 return {success, tokens};239 }240241 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {242 let eventId = null;243 events.forEach(({event: {data, method, section}}) => {244 if ((section === expectedSection) && (method === expectedMethod)) {245 eventId = parseInt(data[0].toString(), 10);246 }247 });248249 if (eventId === null) {250 throw Error(`No ${expectedMethod} event was found!`);251 }252 return eventId === collectionId;253 }254255 static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {256 const normalizeAddress = (address: string | ICrossAccountId) => {257 if(typeof address === 'string') return address;258 const obj = {} as any;259 Object.keys(address).forEach(k => {260 obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];261 });262 if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);263 if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();264 return address;265 };266 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;267 events.forEach(({event: {data, method, section}}) => {268 if ((section === 'common') && (method === 'Transfer')) {269 const hData = (data as any).toJSON();270 transfer = {271 collectionId: hData[0],272 tokenId: hData[1],273 from: normalizeAddress(hData[2]),274 to: normalizeAddress(hData[3]),275 amount: BigInt(hData[4]),276 };277 }278 });279 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;280 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);281 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);282 isSuccess = isSuccess && amount === transfer.amount;283 return isSuccess;284 }285286 static bigIntToDecimals(number: bigint, decimals = 18) {287 const numberStr = number.toString();288 const dotPos = numberStr.length - decimals;289290 if (dotPos <= 0) {291 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;292 } else {293 const intPart = numberStr.substring(0, dotPos);294 const fractPart = numberStr.substring(dotPos);295 return intPart + '.' + fractPart;296 }297 }298}299300class UniqueEventHelper {301 private static extractIndex(index: any): [number, number] | string {302 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];303 return index.toJSON();304 }305306 private static extractSub(data: any, subTypes: any): {[key: string]: any} {307 let obj: any = {};308 let index = 0;309310 if (data.entries) {311 for(const [key, value] of data.entries()) {312 obj[key] = this.extractData(value, subTypes[index]);313 index++;314 }315 } else obj = data.toJSON();316317 return obj;318 }319320 private static extractData(data: any, type: any): any {321 if(!type) return data.toHuman();322 if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();323 if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();324 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);325 return data.toHuman();326 }327328 public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {329 const parsedEvents: IEvent[] = [];330331 events.forEach((record) => {332 const {event, phase} = record;333 const types = event.typeDef;334335 const eventData: IEvent = {336 section: event.section.toString(),337 method: event.method.toString(),338 index: this.extractIndex(event.index),339 data: [],340 phase: phase.toJSON(),341 };342343 event.data.forEach((val: any, index: number) => {344 eventData.data.push(this.extractData(val, types[index]));345 });346347 parsedEvents.push(eventData);348 });349350 return parsedEvents;351 }352}353354export class ChainHelperBase {355 helperBase: any;356357 transactionStatus = UniqueUtil.transactionStatus;358 chainLogType = UniqueUtil.chainLogType;359 util: typeof UniqueUtil;360 eventHelper: typeof UniqueEventHelper;361 logger: ILogger;362 api: ApiPromise | null;363 forcedNetwork: TNetworks | null;364 network: TNetworks | null;365 chainLog: IUniqueHelperLog[];366 children: ChainHelperBase[];367 address: AddressGroup;368 chain: ChainGroup;369370 constructor(logger?: ILogger, helperBase?: any) {371 this.helperBase = helperBase;372373 this.util = UniqueUtil;374 this.eventHelper = UniqueEventHelper;375 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();376 this.logger = logger;377 this.api = null;378 this.forcedNetwork = null;379 this.network = null;380 this.chainLog = [];381 this.children = [];382 this.address = new AddressGroup(this);383 this.chain = new ChainGroup(this);384 }385386 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {387 Object.setPrototypeOf(helperCls.prototype, this);388 const newHelper = new helperCls(this.logger, options);389390 newHelper.api = this.api;391 newHelper.network = this.network;392 newHelper.forceNetwork = this.forceNetwork;393394 this.children.push(newHelper);395396 return newHelper;397 }398399 getApi(): ApiPromise {400 if(this.api === null) throw Error('API not initialized');401 return this.api;402 }403404 clearChainLog(): void {405 this.chainLog = [];406 }407408 forceNetwork(value: TNetworks): void {409 this.forcedNetwork = value;410 }411412 async connect(wsEndpoint: string, listeners?: IApiListeners) {413 if (this.api !== null) throw Error('Already connected');414 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);415 this.api = api;416 this.network = network;417 }418419 async disconnect() {420 for (const child of this.children) {421 child.clearApi();422 }423424 if (this.api === null) return;425 await this.api.disconnect();426 this.clearApi();427 }428429 clearApi() {430 this.api = null;431 this.network = null;432 }433434 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {435 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;436 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];437438 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;439440 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;441 return 'opal';442 }443444 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {445 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});446 await api.isReady;447448 const network = await this.detectNetwork(api);449450 await api.disconnect();451452 return network;453 }454455 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{456 api: ApiPromise;457 network: TNetworks;458 }> {459 if(typeof network === 'undefined' || network === null) network = 'opal';460 const supportedRPC = {461 opal: {462 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,463 },464 quartz: {465 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,466 },467 unique: {468 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,469 },470 rococo: {},471 westend: {},472 moonbeam: {},473 moonriver: {},474 acala: {},475 karura: {},476 westmint: {},477 };478 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);479 const rpc = supportedRPC[network];480481 // TODO: investigate how to replace rpc in runtime482 // api._rpcCore.addUserInterfaces(rpc);483484 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});485486 await api.isReadyOrError;487488 if (typeof listeners === 'undefined') listeners = {};489 for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {490 if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;491 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);492 }493494 return {api, network};495 }496497 getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {498 const {events, status} = data;499 if (status.isReady) {500 return this.transactionStatus.NOT_READY;501 }502 if (status.isBroadcast) {503 return this.transactionStatus.NOT_READY;504 }505 if (status.isInBlock || status.isFinalized) {506 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');507 if (errors.length > 0) {508 return this.transactionStatus.FAIL;509 }510 if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {511 return this.transactionStatus.SUCCESS;512 }513 }514515 return this.transactionStatus.FAIL;516 }517518 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {519 const sign = (callback: any) => {520 if(options !== null) return transaction.signAndSend(sender, options, callback);521 return transaction.signAndSend(sender, callback);522 };523 // eslint-disable-next-line no-async-promise-executor524 return new Promise(async (resolve, reject) => {525 try {526 const unsub = await sign((result: any) => {527 const status = this.getTransactionStatus(result);528529 if (status === this.transactionStatus.SUCCESS) {530 this.logger.log(`${label} successful`);531 unsub();532 resolve({result, status});533 } else if (status === this.transactionStatus.FAIL) {534 let moduleError = null;535536 if (result.hasOwnProperty('dispatchError')) {537 const dispatchError = result['dispatchError'];538539 if (dispatchError) {540 if (dispatchError.isModule) {541 const modErr = dispatchError.asModule;542 const errorMeta = dispatchError.registry.findMetaError(modErr);543544 moduleError = `${errorMeta.section}.${errorMeta.name}`;545 } else {546 moduleError = dispatchError.toHuman();547 }548 } else {549 this.logger.log(result, this.logger.level.ERROR);550 }551 }552553 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);554 unsub();555 reject({status, moduleError, result});556 }557 });558 } catch (e) {559 this.logger.log(e, this.logger.level.ERROR);560 reject(e);561 }562 });563 }564565 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {566 const api = this.getApi();567 const signingInfo = await api.derive.tx.signingInfo(signer.address);568569 // We need to sign the tx because570 // unsigned transactions does not have an inclusion fee571 tx.sign(signer, {572 blockHash: api.genesisHash,573 genesisHash: api.genesisHash,574 runtimeVersion: api.runtimeVersion,575 nonce: signingInfo.nonce,576 });577578 if (len === null) {579 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;580 } else {581 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;582 }583 }584585 constructApiCall(apiCall: string, params: any[]) {586 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);587 let call = this.getApi() as any;588 for(const part of apiCall.slice(4).split('.')) {589 call = call[part];590 }591 return call(...params);592 }593594 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {595 if(this.api === null) throw Error('API not initialized');596 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);597598 const startTime = (new Date()).getTime();599 let result: ITransactionResult;600 let events: IEvent[] = [];601 try {602 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;603 events = this.eventHelper.extractEvents(result.result.events);604 }605 catch(e) {606 if(!(e as object).hasOwnProperty('status')) throw e;607 result = e as ITransactionResult;608 }609610 const endTime = (new Date()).getTime();611612 const log = {613 executedAt: endTime,614 executionTime: endTime - startTime,615 type: this.chainLogType.EXTRINSIC,616 status: result.status,617 call: extrinsic,618 signer: this.getSignerAddress(sender),619 params,620 } as IUniqueHelperLog;621622 if(result.status !== this.transactionStatus.SUCCESS) {623 if (result.moduleError) log.moduleError = result.moduleError;624 else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;625 }626 if(events.length > 0) log.events = events;627628 this.chainLog.push(log);629630 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {631 if (result.moduleError) throw Error(`${result.moduleError}`);632 else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));633 }634 return result;635 }636637 async callRpc(rpc: string, params?: any[]) {638 if(typeof params === 'undefined') params = [];639 if(this.api === null) throw Error('API not initialized');640 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);641642 const startTime = (new Date()).getTime();643 let result;644 let error = null;645 const log = {646 type: this.chainLogType.RPC,647 call: rpc,648 params,649 } as IUniqueHelperLog;650651 try {652 result = await this.constructApiCall(rpc, params);653 }654 catch(e) {655 error = e;656 }657658 const endTime = (new Date()).getTime();659660 log.executedAt = endTime;661 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';662 log.executionTime = endTime - startTime;663664 this.chainLog.push(log);665666 if(error !== null) throw error;667668 return result;669 }670671 getSignerAddress(signer: IKeyringPair | string): string {672 if(typeof signer === 'string') return signer;673 return signer.address;674 }675676 fetchAllPalletNames(): string[] {677 if(this.api === null) throw Error('API not initialized');678 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());679 }680681 fetchMissingPalletNames(requiredPallets: string[]): string[] {682 const palletNames = this.fetchAllPalletNames();683 return requiredPallets.filter(p => !palletNames.includes(p));684 }685}686687688class HelperGroup<T extends ChainHelperBase> {689 helper: T;690691 constructor(uniqueHelper: T) {692 this.helper = uniqueHelper;693 }694}695696697class CollectionGroup extends HelperGroup<UniqueHelper> {698 /**699 * Get number of blocks when sponsored transaction is available.700 *701 * @param collectionId ID of collection702 * @param tokenId ID of token703 * @param addressObj address for which the sponsorship is checked704 * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});705 * @returns number of blocks or null if sponsorship hasn't been set706 */707 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {708 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();709 }710711 /**712 * Get the number of created collections.713 *714 * @returns number of created collections715 */716 async getTotalCount(): Promise<number> {717 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();718 }719720 /**721 * Get information about the collection with additional data,722 * including the number of tokens it contains, its administrators,723 * the normalized address of the collection's owner, and decoded name and description.724 *725 * @param collectionId ID of collection726 * @example await getData(2)727 * @returns collection information object728 */729 async getData(collectionId: number): Promise<{730 id: number;731 name: string;732 description: string;733 tokensCount: number;734 admins: CrossAccountId[];735 normalizedOwner: TSubstrateAccount;736 raw: any737 } | null> {738 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);739 const humanCollection = collection.toHuman(), collectionData = {740 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],741 raw: humanCollection,742 } as any, jsonCollection = collection.toJSON();743 if (humanCollection === null) return null;744 collectionData.raw.limits = jsonCollection.limits;745 collectionData.raw.permissions = jsonCollection.permissions;746 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);747 for (const key of ['name', 'description']) {748 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);749 }750751 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))752 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)753 : 0;754 collectionData.admins = await this.getAdmins(collectionId);755756 return collectionData;757 }758759 /**760 * Get the addresses of the collection's administrators, optionally normalized.761 *762 * @param collectionId ID of collection763 * @param normalize whether to normalize the addresses to the default ss58 format764 * @example await getAdmins(1)765 * @returns array of administrators766 */767 async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {768 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();769770 return normalize771 ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())772 : admins;773 }774775 /**776 * Get the addresses added to the collection allow-list, optionally normalized.777 * @param collectionId ID of collection778 * @param normalize whether to normalize the addresses to the default ss58 format779 * @example await getAllowList(1)780 * @returns array of allow-listed addresses781 */782 async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {783 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();784 return normalize785 ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())786 : allowListed;787 }788789 /**790 * Get the effective limits of the collection instead of null for default values791 *792 * @param collectionId ID of collection793 * @example await getEffectiveLimits(2)794 * @returns object of collection limits795 */796 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {797 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();798 }799800 /**801 * Burns the collection if the signer has sufficient permissions and collection is empty.802 *803 * @param signer keyring of signer804 * @param collectionId ID of collection805 * @example await helper.collection.burn(aliceKeyring, 3);806 * @returns ```true``` if extrinsic success, otherwise ```false```807 */808 async burn(signer: TSigner, collectionId: number): Promise<boolean> {809 const result = await this.helper.executeExtrinsic(810 signer,811 'api.tx.unique.destroyCollection', [collectionId],812 true,813 );814815 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');816 }817818 /**819 * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.820 *821 * @param signer keyring of signer822 * @param collectionId ID of collection823 * @param sponsorAddress Sponsor substrate address824 * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")825 * @returns ```true``` if extrinsic success, otherwise ```false```826 */827 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {828 const result = await this.helper.executeExtrinsic(829 signer,830 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],831 true,832 );833834 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet');835 }836837 /**838 * Confirms consent to sponsor the collection on behalf of the signer.839 *840 * @param signer keyring of signer841 * @param collectionId ID of collection842 * @example confirmSponsorship(aliceKeyring, 10)843 * @returns ```true``` if extrinsic success, otherwise ```false```844 */845 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {846 const result = await this.helper.executeExtrinsic(847 signer,848 'api.tx.unique.confirmSponsorship', [collectionId],849 true,850 );851852 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed');853 }854855 /**856 * Removes the sponsor of a collection, regardless if it consented or not.857 *858 * @param signer keyring of signer859 * @param collectionId ID of collection860 * @example removeSponsor(aliceKeyring, 10)861 * @returns ```true``` if extrinsic success, otherwise ```false```862 */863 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {864 const result = await this.helper.executeExtrinsic(865 signer,866 'api.tx.unique.removeCollectionSponsor', [collectionId],867 true,868 );869870 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorRemoved');871 }872873 /**874 * Sets the limits of the collection. At least one limit must be specified for a correct call.875 *876 * @param signer keyring of signer877 * @param collectionId ID of collection878 * @param limits collection limits object879 * @example880 * await setLimits(881 * aliceKeyring,882 * 10,883 * {884 * sponsorTransferTimeout: 0,885 * ownerCanDestroy: false886 * }887 * )888 * @returns ```true``` if extrinsic success, otherwise ```false```889 */890 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {891 const result = await this.helper.executeExtrinsic(892 signer,893 'api.tx.unique.setCollectionLimits', [collectionId, limits],894 true,895 );896897 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet');898 }899900 /**901 * Changes the owner of the collection to the new Substrate address.902 *903 * @param signer keyring of signer904 * @param collectionId ID of collection905 * @param ownerAddress substrate address of new owner906 * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")907 * @returns ```true``` if extrinsic success, otherwise ```false```908 */909 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {910 const result = await this.helper.executeExtrinsic(911 signer,912 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],913 true,914 );915916 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged');917 }918919 /**920 * Adds a collection administrator.921 *922 * @param signer keyring of signer923 * @param collectionId ID of collection924 * @param adminAddressObj Administrator address (substrate or ethereum)925 * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})926 * @returns ```true``` if extrinsic success, otherwise ```false```927 */928 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {929 const result = await this.helper.executeExtrinsic(930 signer,931 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],932 true,933 );934935 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded');936 }937938 /**939 * Removes a collection administrator.940 *941 * @param signer keyring of signer942 * @param collectionId ID of collection943 * @param adminAddressObj Administrator address (substrate or ethereum)944 * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})945 * @returns ```true``` if extrinsic success, otherwise ```false```946 */947 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {948 const result = await this.helper.executeExtrinsic(949 signer,950 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],951 true,952 );953954 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved');955 }956957 /**958 * Check if user is in allow list.959 *960 * @param collectionId ID of collection961 * @param user Account to check962 * @example await getAdmins(1)963 * @returns is user in allow list964 */965 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {966 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();967 }968969 /**970 * Adds an address to allow list971 * @param signer keyring of signer972 * @param collectionId ID of collection973 * @param addressObj address to add to the allow list974 * @returns ```true``` if extrinsic success, otherwise ```false```975 */976 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {977 const result = await this.helper.executeExtrinsic(978 signer,979 'api.tx.unique.addToAllowList', [collectionId, addressObj],980 true,981 );982983 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressAdded');984 }985986 /**987 * Removes an address from allow list988 *989 * @param signer keyring of signer990 * @param collectionId ID of collection991 * @param addressObj address to remove from the allow list992 * @returns ```true``` if extrinsic success, otherwise ```false```993 */994 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {995 const result = await this.helper.executeExtrinsic(996 signer,997 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],998 true,999 );10001001 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved');1002 }10031004 /**1005 * Sets onchain permissions for selected collection.1006 *1007 * @param signer keyring of signer1008 * @param collectionId ID of collection1009 * @param permissions collection permissions object1010 * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});1011 * @returns ```true``` if extrinsic success, otherwise ```false```1012 */1013 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1014 const result = await this.helper.executeExtrinsic(1015 signer,1016 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1017 true,1018 );10191020 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet');1021 }10221023 /**1024 * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.1025 *1026 * @param signer keyring of signer1027 * @param collectionId ID of collection1028 * @param permissions nesting permissions object1029 * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});1030 * @returns ```true``` if extrinsic success, otherwise ```false```1031 */1032 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1033 return await this.setPermissions(signer, collectionId, {nesting: permissions});1034 }10351036 /**1037 * Disables nesting for selected collection.1038 *1039 * @param signer keyring of signer1040 * @param collectionId ID of collection1041 * @example disableNesting(aliceKeyring, 10);1042 * @returns ```true``` if extrinsic success, otherwise ```false```1043 */1044 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1045 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1046 }10471048 /**1049 * Sets onchain properties to the collection.1050 *1051 * @param signer keyring of signer1052 * @param collectionId ID of collection1053 * @param properties array of property objects1054 * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);1055 * @returns ```true``` if extrinsic success, otherwise ```false```1056 */1057 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1058 const result = await this.helper.executeExtrinsic(1059 signer,1060 'api.tx.unique.setCollectionProperties', [collectionId, properties],1061 true,1062 );10631064 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1065 }10661067 /**1068 * Get collection properties.1069 *1070 * @param collectionId ID of collection1071 * @param propertyKeys optionally filter the returned properties to only these keys1072 * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);1073 * @returns array of key-value pairs1074 */1075 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1076 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1077 }10781079 async getCollectionOptions(collectionId: number) {1080 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1081 }10821083 /**1084 * Deletes onchain properties from the collection.1085 *1086 * @param signer keyring of signer1087 * @param collectionId ID of collection1088 * @param propertyKeys array of property keys to delete1089 * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);1090 * @returns ```true``` if extrinsic success, otherwise ```false```1091 */1092 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1093 const result = await this.helper.executeExtrinsic(1094 signer,1095 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1096 true,1097 );10981099 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1100 }11011102 /**1103 * Changes the owner of the token.1104 *1105 * @param signer keyring of signer1106 * @param collectionId ID of collection1107 * @param tokenId ID of token1108 * @param addressObj address of a new owner1109 * @param amount amount of tokens to be transfered. For NFT must be set to 1n1110 * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1111 * @returns true if the token success, otherwise false1112 */1113 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1114 const result = await this.helper.executeExtrinsic(1115 signer,1116 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1117 true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1118 );11191120 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1121 }11221123 /**1124 *1125 * Change ownership of a token(s) on behalf of the owner.1126 *1127 * @param signer keyring of signer1128 * @param collectionId ID of collection1129 * @param tokenId ID of token1130 * @param fromAddressObj address on behalf of which the token will be sent1131 * @param toAddressObj new token owner1132 * @param amount amount of tokens to be transfered. For NFT must be set to 1n1133 * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})1134 * @returns true if the token success, otherwise false1135 */1136 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1137 const result = await this.helper.executeExtrinsic(1138 signer,1139 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1140 true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1141 );1142 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1143 }11441145 /**1146 *1147 * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.1148 *1149 * @param signer keyring of signer1150 * @param collectionId ID of collection1151 * @param tokenId ID of token1152 * @param amount amount of tokens to be burned. For NFT must be set to 1n1153 * @example burnToken(aliceKeyring, 10, 5);1154 * @returns ```true``` if the extrinsic is successful, otherwise ```false```1155 */1156 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1157 const burnResult = await this.helper.executeExtrinsic(1158 signer,1159 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1160 true, // `Unable to burn token for ${label}`,1161 );1162 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1163 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1164 return burnedTokens.success;1165 }11661167 /**1168 * Destroys a concrete instance of NFT on behalf of the owner1169 *1170 * @param signer keyring of signer1171 * @param collectionId ID of collection1172 * @param tokenId ID of token1173 * @param fromAddressObj address on behalf of which the token will be burnt1174 * @param amount amount of tokens to be burned. For NFT must be set to 1n1175 * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})1176 * @returns ```true``` if extrinsic success, otherwise ```false```1177 */1178 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1179 const burnResult = await this.helper.executeExtrinsic(1180 signer,1181 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1182 true, // `Unable to burn token from for ${label}`,1183 );1184 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1185 return burnedTokens.success && burnedTokens.tokens.length > 0;1186 }11871188 /**1189 * Set, change, or remove approved address to transfer the ownership of the NFT.1190 *1191 * @param signer keyring of signer1192 * @param collectionId ID of collection1193 * @param tokenId ID of token1194 * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens1195 * @param amount amount of token to be approved. For NFT must be set to 1n1196 * @returns ```true``` if extrinsic success, otherwise ```false```1197 */1198 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1199 const approveResult = await this.helper.executeExtrinsic(1200 signer,1201 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1202 true, // `Unable to approve token for ${label}`,1203 );12041205 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1206 }12071208 /**1209 * Get the amount of token pieces approved to transfer or burn. Normally 0.1210 *1211 * @param collectionId ID of collection1212 * @param tokenId ID of token1213 * @param toAccountObj address which is approved to use token pieces1214 * @param fromAccountObj address which may have allowed the use of its owned tokens1215 * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})1216 * @returns number of approved to transfer pieces1217 */1218 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1219 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1220 }12211222 /**1223 * Get the last created token ID in a collection1224 *1225 * @param collectionId ID of collection1226 * @example getLastTokenId(10);1227 * @returns id of the last created token1228 */1229 async getLastTokenId(collectionId: number): Promise<number> {1230 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1231 }12321233 /**1234 * Check if token exists1235 *1236 * @param collectionId ID of collection1237 * @param tokenId ID of token1238 * @example doesTokenExist(10, 20);1239 * @returns true if the token exists, otherwise false1240 */1241 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1242 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1243 }1244}12451246class NFTnRFT extends CollectionGroup {1247 /**1248 * Get tokens owned by account1249 *1250 * @param collectionId ID of collection1251 * @param addressObj tokens owner1252 * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})1253 * @returns array of token ids owned by account1254 */1255 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1256 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1257 }12581259 /**1260 * Get token data1261 *1262 * @param collectionId ID of collection1263 * @param tokenId ID of token1264 * @param propertyKeys optionally filter the token properties to only these keys1265 * @param blockHashAt optionally query the data at some block with this hash1266 * @example getToken(10, 5);1267 * @returns human readable token data1268 */1269 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1270 properties: IProperty[];1271 owner: CrossAccountId;1272 normalizedOwner: CrossAccountId;1273 }| null> {1274 let tokenData;1275 if(typeof blockHashAt === 'undefined') {1276 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1277 }1278 else {1279 if(propertyKeys.length == 0) {1280 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1281 if(!collection) return null;1282 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1283 }1284 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1285 }1286 tokenData = tokenData.toHuman();1287 if (tokenData === null || tokenData.owner === null) return null;1288 const owner = {} as any;1289 for (const key of Object.keys(tokenData.owner)) {1290 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1291 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1292 : tokenData.owner[key];1293 }1294 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1295 return tokenData;1296 }12971298 /**1299 * Set permissions to change token properties1300 *1301 * @param signer keyring of signer1302 * @param collectionId ID of collection1303 * @param permissions permissions to change a property by the collection admin or token owner1304 * @example setTokenPropertyPermissions(1305 * aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]1306 * )1307 * @returns true if extrinsic success otherwise false1308 */1309 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1310 const result = await this.helper.executeExtrinsic(1311 signer,1312 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1313 true,1314 );13151316 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1317 }13181319 /**1320 * Get token property permissions.1321 *1322 * @param collectionId ID of collection1323 * @param propertyKeys optionally filter the returned property permissions to only these keys1324 * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);1325 * @returns array of key-permission pairs1326 */1327 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1328 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1329 }13301331 /**1332 * Set token properties1333 *1334 * @param signer keyring of signer1335 * @param collectionId ID of collection1336 * @param tokenId ID of token1337 * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection1338 * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])1339 * @returns ```true``` if extrinsic success, otherwise ```false```1340 */1341 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1342 const result = await this.helper.executeExtrinsic(1343 signer,1344 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1345 true,1346 );13471348 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1349 }13501351 /**1352 * Get properties, metadata assigned to a token.1353 *1354 * @param collectionId ID of collection1355 * @param tokenId ID of token1356 * @param propertyKeys optionally filter the returned properties to only these keys1357 * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);1358 * @returns array of key-value pairs1359 */1360 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1361 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1362 }13631364 /**1365 * Delete the provided properties of a token1366 * @param signer keyring of signer1367 * @param collectionId ID of collection1368 * @param tokenId ID of token1369 * @param propertyKeys property keys to be deleted1370 * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])1371 * @returns ```true``` if extrinsic success, otherwise ```false```1372 */1373 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1374 const result = await this.helper.executeExtrinsic(1375 signer,1376 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1377 true,1378 );13791380 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1381 }13821383 /**1384 * Mint new collection1385 *1386 * @param signer keyring of signer1387 * @param collectionOptions basic collection options and properties1388 * @param mode NFT or RFT type of a collection1389 * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")1390 * @returns object of the created collection1391 */1392 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1393 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1394 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1395 for (const key of ['name', 'description', 'tokenPrefix']) {1396 if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1397 }1398 const creationResult = await this.helper.executeExtrinsic(1399 signer,1400 'api.tx.unique.createCollectionEx', [collectionOptions],1401 true, // errorLabel,1402 );1403 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1404 }14051406 getCollectionObject(_collectionId: number): any {1407 return null;1408 }14091410 getTokenObject(_collectionId: number, _tokenId: number): any {1411 return null;1412 }1413}141414151416class NFTGroup extends NFTnRFT {1417 /**1418 * Get collection object1419 * @param collectionId ID of collection1420 * @example getCollectionObject(2);1421 * @returns instance of UniqueNFTCollection1422 */1423 getCollectionObject(collectionId: number): UniqueNFTCollection {1424 return new UniqueNFTCollection(collectionId, this.helper);1425 }14261427 /**1428 * Get token object1429 * @param collectionId ID of collection1430 * @param tokenId ID of token1431 * @example getTokenObject(10, 5);1432 * @returns instance of UniqueNFTToken1433 */1434 getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1435 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1436 }14371438 /**1439 * Get token's owner1440 * @param collectionId ID of collection1441 * @param tokenId ID of token1442 * @param blockHashAt optionally query the data at the block with this hash1443 * @example getTokenOwner(10, 5);1444 * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}1445 */1446 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1447 let owner;1448 if (typeof blockHashAt === 'undefined') {1449 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1450 } else {1451 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1452 }1453 return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1454 }14551456 /**1457 * Is token approved to transfer1458 * @param collectionId ID of collection1459 * @param tokenId ID of token1460 * @param toAccountObj address to be approved1461 * @returns ```true``` if extrinsic success, otherwise ```false```1462 */1463 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1464 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1465 }14661467 /**1468 * Changes the owner of the token.1469 *1470 * @param signer keyring of signer1471 * @param collectionId ID of collection1472 * @param tokenId ID of token1473 * @param addressObj address of a new owner1474 * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1475 * @returns ```true``` if extrinsic success, otherwise ```false```1476 */1477 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1478 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1479 }14801481 /**1482 *1483 * Change ownership of a NFT on behalf of the owner.1484 *1485 * @param signer keyring of signer1486 * @param collectionId ID of collection1487 * @param tokenId ID of token1488 * @param fromAddressObj address on behalf of which the token will be sent1489 * @param toAddressObj new token owner1490 * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})1491 * @returns ```true``` if extrinsic success, otherwise ```false```1492 */1493 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1494 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1495 }14961497 /**1498 * Recursively find the address that owns the token1499 * @param collectionId ID of collection1500 * @param tokenId ID of token1501 * @param blockHashAt1502 * @example getTokenTopmostOwner(10, 5);1503 * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}1504 */1505 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1506 let owner;1507 if (typeof blockHashAt === 'undefined') {1508 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1509 } else {1510 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1511 }15121513 if (owner === null) return null;15141515 return owner.toHuman();1516 }15171518 /**1519 * Get tokens nested in the provided token1520 * @param collectionId ID of collection1521 * @param tokenId ID of token1522 * @param blockHashAt optionally query the data at the block with this hash1523 * @example getTokenChildren(10, 5);1524 * @returns tokens whose depth of nesting is <= 51525 */1526 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1527 let children;1528 if(typeof blockHashAt === 'undefined') {1529 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1530 } else {1531 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1532 }15331534 return children.toJSON().map((x: any) => {1535 return {collectionId: x.collection, tokenId: x.token};1536 });1537 }15381539 /**1540 * Nest one token into another1541 * @param signer keyring of signer1542 * @param tokenObj token to be nested1543 * @param rootTokenObj token to be parent1544 * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});1545 * @returns ```true``` if extrinsic success, otherwise ```false```1546 */1547 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1548 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1549 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1550 if(!result) {1551 throw Error('Unable to nest token!');1552 }1553 return result;1554 }15551556 /**1557 * Remove token from nested state1558 * @param signer keyring of signer1559 * @param tokenObj token to unnest1560 * @param rootTokenObj parent of a token1561 * @param toAddressObj address of a new token owner1562 * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});1563 * @returns ```true``` if extrinsic success, otherwise ```false```1564 */1565 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1566 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1567 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1568 if(!result) {1569 throw Error('Unable to unnest token!');1570 }1571 return result;1572 }15731574 /**1575 * Mint new collection1576 * @param signer keyring of signer1577 * @param collectionOptions Collection options1578 * @example1579 * mintCollection(aliceKeyring, {1580 * name: 'New',1581 * description: 'New collection',1582 * tokenPrefix: 'NEW',1583 * })1584 * @returns object of the created collection1585 */1586 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1587 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1588 }15891590 /**1591 * Mint new token1592 * @param signer keyring of signer1593 * @param data token data1594 * @returns created token object1595 */1596 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1597 const creationResult = await this.helper.executeExtrinsic(1598 signer,1599 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1600 nft: {1601 properties: data.properties,1602 },1603 }],1604 true,1605 );1606 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1607 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1608 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1609 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1610 }16111612 /**1613 * Mint multiple NFT tokens1614 * @param signer keyring of signer1615 * @param collectionId ID of collection1616 * @param tokens array of tokens with owner and properties1617 * @example1618 * mintMultipleTokens(aliceKeyring, 10, [{1619 * owner: {Substrate: "5DyN4Y92vZCjv38fg..."},1620 * properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],1621 * },{1622 * owner: {Ethereum: "0x9F0583DbB855d..."},1623 * properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],1624 * }]);1625 * @returns ```true``` if extrinsic success, otherwise ```false```1626 */1627 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1628 const creationResult = await this.helper.executeExtrinsic(1629 signer,1630 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1631 true,1632 );1633 const collection = this.getCollectionObject(collectionId);1634 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1635 }16361637 /**1638 * Mint multiple NFT tokens with one owner1639 * @param signer keyring of signer1640 * @param collectionId ID of collection1641 * @param owner tokens owner1642 * @param tokens array of tokens with owner and properties1643 * @example1644 * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{1645 * properties: [{1646 * key: "gender",1647 * value: "female",1648 * },{1649 * key: "age",1650 * value: "33",1651 * }],1652 * }]);1653 * @returns array of newly created tokens1654 */1655 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1656 const rawTokens = [];1657 for (const token of tokens) {1658 const raw = {NFT: {properties: token.properties}};1659 rawTokens.push(raw);1660 }1661 const creationResult = await this.helper.executeExtrinsic(1662 signer,1663 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1664 true,1665 );1666 const collection = this.getCollectionObject(collectionId);1667 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1668 }16691670 /**1671 * Set, change, or remove approved address to transfer the ownership of the NFT.1672 *1673 * @param signer keyring of signer1674 * @param collectionId ID of collection1675 * @param tokenId ID of token1676 * @param toAddressObj address to approve1677 * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1678 * @returns ```true``` if extrinsic success, otherwise ```false```1679 */1680 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1681 return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1682 }1683}168416851686class RFTGroup extends NFTnRFT {1687 /**1688 * Get collection object1689 * @param collectionId ID of collection1690 * @example getCollectionObject(2);1691 * @returns instance of UniqueRFTCollection1692 */1693 getCollectionObject(collectionId: number): UniqueRFTCollection {1694 return new UniqueRFTCollection(collectionId, this.helper);1695 }16961697 /**1698 * Get token object1699 * @param collectionId ID of collection1700 * @param tokenId ID of token1701 * @example getTokenObject(10, 5);1702 * @returns instance of UniqueNFTToken1703 */1704 getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1705 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1706 }17071708 /**1709 * Get top 10 token owners with the largest number of pieces1710 * @param collectionId ID of collection1711 * @param tokenId ID of token1712 * @example getTokenTop10Owners(10, 5);1713 * @returns array of top 10 owners1714 */1715 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1716 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1717 }17181719 /**1720 * Get number of pieces owned by address1721 * @param collectionId ID of collection1722 * @param tokenId ID of token1723 * @param addressObj address token owner1724 * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});1725 * @returns number of pieces ownerd by address1726 */1727 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1728 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1729 }17301731 /**1732 * Transfer pieces of token to another address1733 * @param signer keyring of signer1734 * @param collectionId ID of collection1735 * @param tokenId ID of token1736 * @param addressObj address of a new owner1737 * @param amount number of pieces to be transfered1738 * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)1739 * @returns ```true``` if extrinsic success, otherwise ```false```1740 */1741 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1742 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1743 }17441745 /**1746 * Change ownership of some pieces of RFT on behalf of the owner.1747 * @param signer keyring of signer1748 * @param collectionId ID of collection1749 * @param tokenId ID of token1750 * @param fromAddressObj address on behalf of which the token will be sent1751 * @param toAddressObj new token owner1752 * @param amount number of pieces to be transfered1753 * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)1754 * @returns ```true``` if extrinsic success, otherwise ```false```1755 */1756 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1757 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1758 }17591760 /**1761 * Mint new collection1762 * @param signer keyring of signer1763 * @param collectionOptions Collection options1764 * @example1765 * mintCollection(aliceKeyring, {1766 * name: 'New',1767 * description: 'New collection',1768 * tokenPrefix: 'NEW',1769 * })1770 * @returns object of the created collection1771 */1772 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1773 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1774 }17751776 /**1777 * Mint new token1778 * @param signer keyring of signer1779 * @param data token data1780 * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});1781 * @returns created token object1782 */1783 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1784 const creationResult = await this.helper.executeExtrinsic(1785 signer,1786 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1787 refungible: {1788 pieces: data.pieces,1789 properties: data.properties,1790 },1791 }],1792 true,1793 );1794 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1795 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1796 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1797 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1798 }17991800 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1801 throw Error('Not implemented');1802 const creationResult = await this.helper.executeExtrinsic(1803 signer,1804 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1805 true, // `Unable to mint RFT tokens for ${label}`,1806 );1807 const collection = this.getCollectionObject(collectionId);1808 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1809 }18101811 /**1812 * Mint multiple RFT tokens with one owner1813 * @param signer keyring of signer1814 * @param collectionId ID of collection1815 * @param owner tokens owner1816 * @param tokens array of tokens with properties and pieces1817 * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);1818 * @returns array of newly created RFT tokens1819 */1820 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1821 const rawTokens = [];1822 for (const token of tokens) {1823 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1824 rawTokens.push(raw);1825 }1826 const creationResult = await this.helper.executeExtrinsic(1827 signer,1828 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1829 true,1830 );1831 const collection = this.getCollectionObject(collectionId);1832 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1833 }18341835 /**1836 * Destroys a concrete instance of RFT.1837 * @param signer keyring of signer1838 * @param collectionId ID of collection1839 * @param tokenId ID of token1840 * @param amount number of pieces to be burnt1841 * @example burnToken(aliceKeyring, 10, 5);1842 * @returns ```true``` if the extrinsic is successful, otherwise ```false```1843 */1844 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1845 return await super.burnToken(signer, collectionId, tokenId, amount);1846 }18471848 /**1849 * Destroys a concrete instance of RFT on behalf of the owner.1850 * @param signer keyring of signer1851 * @param collectionId ID of collection1852 * @param tokenId ID of token1853 * @param fromAddressObj address on behalf of which the token will be burnt1854 * @param amount number of pieces to be burnt1855 * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)1856 * @returns ```true``` if extrinsic success, otherwise ```false```1857 */1858 async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1859 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1860 }18611862 /**1863 * Set, change, or remove approved address to transfer the ownership of the RFT.1864 *1865 * @param signer keyring of signer1866 * @param collectionId ID of collection1867 * @param tokenId ID of token1868 * @param toAddressObj address to approve1869 * @param amount number of pieces to be approved1870 * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);1871 * @returns true if the token success, otherwise false1872 */1873 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1874 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1875 }18761877 /**1878 * Get total number of pieces1879 * @param collectionId ID of collection1880 * @param tokenId ID of token1881 * @example getTokenTotalPieces(10, 5);1882 * @returns number of pieces1883 */1884 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1885 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1886 }18871888 /**1889 * Change number of token pieces. Signer must be the owner of all token pieces.1890 * @param signer keyring of signer1891 * @param collectionId ID of collection1892 * @param tokenId ID of token1893 * @param amount new number of pieces1894 * @example repartitionToken(aliceKeyring, 10, 5, 12345n);1895 * @returns true if the repartion was success, otherwise false1896 */1897 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1898 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1899 const repartitionResult = await this.helper.executeExtrinsic(1900 signer,1901 'api.tx.unique.repartition', [collectionId, tokenId, amount],1902 true,1903 );1904 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1905 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1906 }1907}190819091910class FTGroup extends CollectionGroup {1911 /**1912 * Get collection object1913 * @param collectionId ID of collection1914 * @example getCollectionObject(2);1915 * @returns instance of UniqueFTCollection1916 */1917 getCollectionObject(collectionId: number): UniqueFTCollection {1918 return new UniqueFTCollection(collectionId, this.helper);1919 }19201921 /**1922 * Mint new fungible collection1923 * @param signer keyring of signer1924 * @param collectionOptions Collection options1925 * @param decimalPoints number of token decimals1926 * @example1927 * mintCollection(aliceKeyring, {1928 * name: 'New',1929 * description: 'New collection',1930 * tokenPrefix: 'NEW',1931 * }, 18)1932 * @returns newly created fungible collection1933 */1934 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1935 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1936 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1937 collectionOptions.mode = {fungible: decimalPoints};1938 for (const key of ['name', 'description', 'tokenPrefix']) {1939 if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1940 }1941 const creationResult = await this.helper.executeExtrinsic(1942 signer,1943 'api.tx.unique.createCollectionEx', [collectionOptions],1944 true,1945 );1946 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1947 }19481949 /**1950 * Mint tokens1951 * @param signer keyring of signer1952 * @param collectionId ID of collection1953 * @param owner address owner of new tokens1954 * @param amount amount of tokens to be meanted1955 * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);1956 * @returns ```true``` if extrinsic success, otherwise ```false```1957 */1958 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {1959 const creationResult = await this.helper.executeExtrinsic(1960 signer,1961 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {1962 fungible: {1963 value: amount,1964 },1965 }],1966 true, // `Unable to mint fungible tokens for ${label}`,1967 );1968 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1969 }19701971 /**1972 * Mint multiple Fungible tokens with one owner1973 * @param signer keyring of signer1974 * @param collectionId ID of collection1975 * @param owner tokens owner1976 * @param tokens array of tokens with properties and pieces1977 * @returns ```true``` if extrinsic success, otherwise ```false```1978 */1979 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {1980 const rawTokens = [];1981 for (const token of tokens) {1982 const raw = {Fungible: {Value: token.value}};1983 rawTokens.push(raw);1984 }1985 const creationResult = await this.helper.executeExtrinsic(1986 signer,1987 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1988 true,1989 );1990 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1991 }19921993 /**1994 * Get the top 10 owners with the largest balance for the Fungible collection1995 * @param collectionId ID of collection1996 * @example getTop10Owners(10);1997 * @returns array of ```ICrossAccountId```1998 */1999 async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2000 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2001 }20022003 /**2004 * Get account balance2005 * @param collectionId ID of collection2006 * @param addressObj address of owner2007 * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})2008 * @returns amount of fungible tokens owned by address2009 */2010 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2011 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2012 }20132014 /**2015 * Transfer tokens to address2016 * @param signer keyring of signer2017 * @param collectionId ID of collection2018 * @param toAddressObj address recipient2019 * @param amount amount of tokens to be sent2020 * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2021 * @returns ```true``` if extrinsic success, otherwise ```false```2022 */2023 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2024 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2025 }20262027 /**2028 * Transfer some tokens on behalf of the owner.2029 * @param signer keyring of signer2030 * @param collectionId ID of collection2031 * @param fromAddressObj address on behalf of which tokens will be sent2032 * @param toAddressObj address where token to be sent2033 * @param amount number of tokens to be sent2034 * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);2035 * @returns ```true``` if extrinsic success, otherwise ```false```2036 */2037 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2038 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2039 }20402041 /**2042 * Destroy some amount of tokens2043 * @param signer keyring of signer2044 * @param collectionId ID of collection2045 * @param amount amount of tokens to be destroyed2046 * @example burnTokens(aliceKeyring, 10, 1000n);2047 * @returns ```true``` if extrinsic success, otherwise ```false```2048 */2049 async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2050 return await super.burnToken(signer, collectionId, 0, amount);2051 }20522053 /**2054 * Burn some tokens on behalf of the owner.2055 * @param signer keyring of signer2056 * @param collectionId ID of collection2057 * @param fromAddressObj address on behalf of which tokens will be burnt2058 * @param amount amount of tokens to be burnt2059 * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2060 * @returns ```true``` if extrinsic success, otherwise ```false```2061 */2062 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2063 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2064 }20652066 /**2067 * Get total collection supply2068 * @param collectionId2069 * @returns2070 */2071 async getTotalPieces(collectionId: number): Promise<bigint> {2072 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2073 }20742075 /**2076 * Set, change, or remove approved address to transfer tokens.2077 *2078 * @param signer keyring of signer2079 * @param collectionId ID of collection2080 * @param toAddressObj address to be approved2081 * @param amount amount of tokens to be approved2082 * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)2083 * @returns ```true``` if extrinsic success, otherwise ```false```2084 */2085 async approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2086 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2087 }20882089 /**2090 * Get amount of fungible tokens approved to transfer2091 * @param collectionId ID of collection2092 * @param fromAddressObj owner of tokens2093 * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner2094 * @returns number of tokens approved for the transfer2095 */2096 async getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2097 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2098 }2099}210021012102class ChainGroup extends HelperGroup<ChainHelperBase> {2103 /**2104 * Get system properties of a chain2105 * @example getChainProperties();2106 * @returns ss58Format, token decimals, and token symbol2107 */2108 getChainProperties(): IChainProperties {2109 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2110 return {2111 ss58Format: properties.ss58Format.toJSON(),2112 tokenDecimals: properties.tokenDecimals.toJSON(),2113 tokenSymbol: properties.tokenSymbol.toJSON(),2114 };2115 }21162117 /**2118 * Get chain header2119 * @example getLatestBlockNumber();2120 * @returns the number of the last block2121 */2122 async getLatestBlockNumber(): Promise<number> {2123 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2124 }21252126 /**2127 * Get block hash by block number2128 * @param blockNumber number of block2129 * @example getBlockHashByNumber(12345);2130 * @returns hash of a block2131 */2132 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2133 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2134 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2135 return blockHash;2136 }21372138 // TODO add docs2139 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2140 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2141 if (!blockHash) return null;2142 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2143 }21442145 /**2146 * Get account nonce2147 * @param address substrate address2148 * @example getNonce("5GrwvaEF5zXb26Fz...");2149 * @returns number, account's nonce2150 */2151 async getNonce(address: TSubstrateAccount): Promise<number> {2152 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2153 }2154}21552156class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2157 /**2158 * Get substrate address balance2159 * @param address substrate address2160 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2161 * @returns amount of tokens on address2162 */2163 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2164 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2165 }21662167 /**2168 * Transfer tokens to substrate address2169 * @param signer keyring of signer2170 * @param address substrate address of a recipient2171 * @param amount amount of tokens to be transfered2172 * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2173 * @returns ```true``` if extrinsic success, otherwise ```false```2174 */2175 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2176 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);21772178 let transfer = {from: null, to: null, amount: 0n} as any;2179 result.result.events.forEach(({event: {data, method, section}}) => {2180 if ((section === 'balances') && (method === 'Transfer')) {2181 transfer = {2182 from: this.helper.address.normalizeSubstrate(data[0]),2183 to: this.helper.address.normalizeSubstrate(data[1]),2184 amount: BigInt(data[2]),2185 };2186 }2187 });2188 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2189 && this.helper.address.normalizeSubstrate(address) === transfer.to2190 && BigInt(amount) === transfer.amount;2191 return isSuccess;2192 }21932194 /**2195 * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2196 * @param address substrate address2197 * @returns2198 */2199 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2200 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2201 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2202 }2203}22042205class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2206 /**2207 * Get ethereum address balance2208 * @param address ethereum address2209 * @example getEthereum("0x9F0583DbB855d...")2210 * @returns amount of tokens on address2211 */2212 async getEthereum(address: TEthereumAccount): Promise<bigint> {2213 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2214 }22152216 /**2217 * Transfer tokens to address2218 * @param signer keyring of signer2219 * @param address Ethereum address of a recipient2220 * @param amount amount of tokens to be transfered2221 * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);2222 * @returns ```true``` if extrinsic success, otherwise ```false```2223 */2224 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2225 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22262227 let transfer = {from: null, to: null, amount: 0n} as any;2228 result.result.events.forEach(({event: {data, method, section}}) => {2229 if ((section === 'balances') && (method === 'Transfer')) {2230 transfer = {2231 from: data[0].toString(),2232 to: data[1].toString(),2233 amount: BigInt(data[2]),2234 };2235 }2236 });2237 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2238 && address === transfer.to2239 && BigInt(amount) === transfer.amount;2240 return isSuccess;2241 }2242}22432244class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2245 subBalanceGroup: SubstrateBalanceGroup<T>;2246 ethBalanceGroup: EthereumBalanceGroup<T>;22472248 constructor(helper: T) {2249 super(helper);2250 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2251 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2252 }22532254 getCollectionCreationPrice(): bigint {2255 return 2n * this.getOneTokenNominal();2256 }2257 /**2258 * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2259 * @example getOneTokenNominal()2260 * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.2261 */2262 getOneTokenNominal(): bigint {2263 const chainProperties = this.helper.chain.getChainProperties();2264 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2265 }22662267 /**2268 * Get substrate address balance2269 * @param address substrate address2270 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2271 * @returns amount of tokens on address2272 */2273 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2274 return this.subBalanceGroup.getSubstrate(address);2275 }22762277 /**2278 * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2279 * @param address substrate address2280 * @returns2281 */2282 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2283 return this.subBalanceGroup.getSubstrateFull(address);2284 }22852286 /**2287 * Get ethereum address balance2288 * @param address ethereum address2289 * @example getEthereum("0x9F0583DbB855d...")2290 * @returns amount of tokens on address2291 */2292 async getEthereum(address: TEthereumAccount): Promise<bigint> {2293 return this.ethBalanceGroup.getEthereum(address);2294 }22952296 /**2297 * Transfer tokens to substrate address2298 * @param signer keyring of signer2299 * @param address substrate address of a recipient2300 * @param amount amount of tokens to be transfered2301 * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2302 * @returns ```true``` if extrinsic success, otherwise ```false```2303 */2304 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2305 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2306 }23072308 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2309 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);23102311 let transfer = {from: null, to: null, amount: 0n} as any;2312 result.result.events.forEach(({event: {data, method, section}}) => {2313 if ((section === 'balances') && (method === 'Transfer')) {2314 transfer = {2315 from: this.helper.address.normalizeSubstrate(data[0]),2316 to: this.helper.address.normalizeSubstrate(data[1]),2317 amount: BigInt(data[2]),2318 };2319 }2320 });2321 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2322 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2323 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2324 return isSuccess;2325 }2326}23272328class AddressGroup extends HelperGroup<ChainHelperBase> {2329 /**2330 * Normalizes the address to the specified ss58 format, by default ```42```.2331 * @param address substrate address2332 * @param ss58Format format for address conversion, by default ```42```2333 * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY2334 * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation2335 */2336 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2337 return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2338 }23392340 /**2341 * Get address in the connected chain format2342 * @param address substrate address2343 * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network2344 * @returns address in chain format2345 */2346 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2347 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2348 }23492350 /**2351 * Get substrate mirror of an ethereum address2352 * @param ethAddress ethereum address2353 * @param toChainFormat false for normalized account2354 * @example ethToSubstrate('0x9F0583DbB855d...')2355 * @returns substrate mirror of a provided ethereum address2356 */2357 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2358 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2359 }23602361 /**2362 * Get ethereum mirror of a substrate address2363 * @param subAddress substrate account2364 * @example substrateToEth("5DnSF6RRjwteE3BrC...")2365 * @returns ethereum mirror of a provided substrate address2366 */2367 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2368 return CrossAccountId.translateSubToEth(subAddress);2369 }23702371 paraSiblingSovereignAccount(paraid: number) {2372 // We are getting a *sibling* parachain sovereign account,2373 // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c2374 const siblingPrefix = '0x7369626c';23752376 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2377 const suffix = '000000000000000000000000000000000000000000000000';23782379 return siblingPrefix + encodedParaId + suffix;2380 }2381}23822383class StakingGroup extends HelperGroup<UniqueHelper> {2384 /**2385 * Stake tokens for App Promotion2386 * @param signer keyring of signer2387 * @param amountToStake amount of tokens to stake2388 * @param label extra label for log2389 * @returns2390 */2391 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2392 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2393 const _stakeResult = await this.helper.executeExtrinsic(2394 signer, 'api.tx.appPromotion.stake',2395 [amountToStake], true,2396 );2397 // TODO extract info from stakeResult2398 return true;2399 }24002401 /**2402 * Unstake tokens for App Promotion2403 * @param signer keyring of signer2404 * @param amountToUnstake amount of tokens to unstake2405 * @param label extra label for log2406 * @returns block number where balances will be unlocked2407 */2408 async unstake(signer: TSigner, label?: string): Promise<number> {2409 if(typeof label === 'undefined') label = `${signer.address}`;2410 const _unstakeResult = await this.helper.executeExtrinsic(2411 signer, 'api.tx.appPromotion.unstake',2412 [], true,2413 );2414 // TODO extract block number fron events2415 return 1;2416 }24172418 /**2419 * Get total staked amount for address2420 * @param address substrate or ethereum address2421 * @returns total staked amount2422 */2423 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2424 if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2425 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2426 }24272428 /**2429 * Get total staked per block2430 * @param address substrate or ethereum address2431 * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block2432 */2433 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2434 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2435 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2436 return {2437 block: block.toBigInt(),2438 amount: amount.toBigInt(),2439 };2440 });2441 }24422443 /**2444 * Get total pending unstake amount for address2445 * @param address substrate or ethereum address2446 * @returns total pending unstake amount2447 */2448 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2449 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2450 }24512452 /**2453 * Get pending unstake amount per block for address2454 * @param address substrate or ethereum address2455 * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block2456 */2457 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2458 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2459 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2460 return {2461 block: block.toBigInt(),2462 amount: amount.toBigInt(),2463 };2464 });2465 return result;2466 }2467}24682469class SchedulerGroup extends HelperGroup<UniqueHelper> {2470 constructor(helper: UniqueHelper) {2471 super(helper);2472 }24732474 async cancelScheduled(signer: TSigner, scheduledId: string) {2475 return this.helper.executeExtrinsic(2476 signer,2477 'api.tx.scheduler.cancelNamed',2478 [scheduledId],2479 true,2480 );2481 }24822483 async changePriority(signer: TSigner, scheduledId: string, priority: number) {2484 return this.helper.executeExtrinsic(2485 signer,2486 'api.tx.scheduler.changeNamedPriority',2487 [scheduledId, priority],2488 true,2489 );2490 }24912492 scheduleAt<T extends UniqueHelper>(2493 scheduledId: string,2494 executionBlockNumber: number,2495 options: ISchedulerOptions = {},2496 ) {2497 return this.schedule<T>('scheduleNamed', scheduledId, executionBlockNumber, options);2498 }24992500 scheduleAfter<T extends UniqueHelper>(2501 scheduledId: string,2502 blocksBeforeExecution: number,2503 options: ISchedulerOptions = {},2504 ) {2505 return this.schedule<T>('scheduleNamedAfter', scheduledId, blocksBeforeExecution, options);2506 }25072508 schedule<T extends UniqueHelper>(2509 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter',2510 scheduledId: string,2511 blocksNum: number,2512 options: ISchedulerOptions = {},2513 ) {2514 // eslint-disable-next-line @typescript-eslint/naming-convention2515 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2516 return this.helper.clone(ScheduledHelperType, {2517 scheduleFn,2518 scheduledId,2519 blocksNum,2520 options,2521 }) as T;2522 }2523}25242525class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2526 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2527 await this.helper.executeExtrinsic(2528 signer,2529 'api.tx.foreignAssets.registerForeignAsset',2530 [ownerAddress, location, metadata],2531 true,2532 );2533 }25342535 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2536 await this.helper.executeExtrinsic(2537 signer,2538 'api.tx.foreignAssets.updateForeignAsset',2539 [foreignAssetId, location, metadata],2540 true,2541 );2542 }2543}25442545class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2546 palletName: string;25472548 constructor(helper: T, palletName: string) {2549 super(helper);25502551 this.palletName = palletName;2552 }25532554 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2555 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2556 }2557}25582559class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2560 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2561 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2562 }25632564 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2565 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2566 }25672568 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2569 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2570 }2571}25722573class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2574 async accounts(address: string, currencyId: any) {2575 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2576 return BigInt(free);2577 }2578}25792580class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2581 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2582 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2583 }25842585 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2586 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2587 }25882589 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2590 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2591 }25922593 async account(assetId: string | number, address: string) {2594 const accountAsset = (2595 await this.helper.callRpc('api.query.assets.account', [assetId, address])2596 ).toJSON()! as any;25972598 if (accountAsset !== null) {2599 return BigInt(accountAsset['balance']);2600 } else {2601 return null;2602 }2603 }2604}26052606class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2607 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2608 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2609 }2610}26112612class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2613 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2614 const apiPrefix = 'api.tx.assetManager.';26152616 const registerTx = this.helper.constructApiCall(2617 apiPrefix + 'registerForeignAsset',2618 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2619 );26202621 const setUnitsTx = this.helper.constructApiCall(2622 apiPrefix + 'setAssetUnitsPerSecond',2623 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2624 );26252626 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2627 const encodedProposal = batchCall?.method.toHex() || '';2628 return encodedProposal;2629 }26302631 async assetTypeId(location: any) {2632 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2633 }2634}26352636class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2637 async notePreimage(signer: TSigner, encodedProposal: string) {2638 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2639 }26402641 externalProposeMajority(proposalHash: string) {2642 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2643 }26442645 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2646 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2647 }26482649 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2650 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2651 }2652}26532654class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2655 collective: string;26562657 constructor(helper: MoonbeamHelper, collective: string) {2658 super(helper);26592660 this.collective = collective;2661 }26622663 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2664 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2665 }26662667 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2668 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2669 }26702671 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2672 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2673 }26742675 async proposalCount() {2676 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2677 }2678}26792680export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2681export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;26822683export class UniqueHelper extends ChainHelperBase {2684 balance: BalanceGroup<UniqueHelper>;2685 collection: CollectionGroup;2686 nft: NFTGroup;2687 rft: RFTGroup;2688 ft: FTGroup;2689 staking: StakingGroup;2690 scheduler: SchedulerGroup;2691 foreignAssets: ForeignAssetsGroup;2692 xcm: XcmGroup<UniqueHelper>;2693 xTokens: XTokensGroup<UniqueHelper>;2694 tokens: TokensGroup<UniqueHelper>;26952696 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2697 super(logger, options.helperBase ?? UniqueHelper);26982699 this.balance = new BalanceGroup(this);2700 this.collection = new CollectionGroup(this);2701 this.nft = new NFTGroup(this);2702 this.rft = new RFTGroup(this);2703 this.ft = new FTGroup(this);2704 this.staking = new StakingGroup(this);2705 this.scheduler = new SchedulerGroup(this);2706 this.foreignAssets = new ForeignAssetsGroup(this);2707 this.xcm = new XcmGroup(this, 'polkadotXcm');2708 this.xTokens = new XTokensGroup(this);2709 this.tokens = new TokensGroup(this);2710 }27112712 getSudo<T extends UniqueHelper>() {2713 // eslint-disable-next-line @typescript-eslint/naming-convention2714 const SudoHelperType = SudoHelper(this.helperBase);2715 return this.clone(SudoHelperType) as T;2716 }2717}27182719export class XcmChainHelper extends ChainHelperBase {2720 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2721 const wsProvider = new WsProvider(wsEndpoint);2722 this.api = new ApiPromise({2723 provider: wsProvider,2724 });2725 await this.api.isReadyOrError;2726 this.network = await UniqueHelper.detectNetwork(this.api);2727 }2728}27292730export class RelayHelper extends XcmChainHelper {2731 xcm: XcmGroup<RelayHelper>;27322733 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2734 super(logger, options.helperBase ?? RelayHelper);27352736 this.xcm = new XcmGroup(this, 'xcmPallet');2737 }2738}27392740export class WestmintHelper extends XcmChainHelper {2741 balance: SubstrateBalanceGroup<WestmintHelper>;2742 xcm: XcmGroup<WestmintHelper>;2743 assets: AssetsGroup<WestmintHelper>;2744 xTokens: XTokensGroup<WestmintHelper>;27452746 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2747 super(logger, options.helperBase ?? WestmintHelper);27482749 this.balance = new SubstrateBalanceGroup(this);2750 this.xcm = new XcmGroup(this, 'polkadotXcm');2751 this.assets = new AssetsGroup(this);2752 this.xTokens = new XTokensGroup(this);2753 }2754}27552756export class MoonbeamHelper extends XcmChainHelper {2757 balance: EthereumBalanceGroup<MoonbeamHelper>;2758 assetManager: MoonbeamAssetManagerGroup;2759 assets: AssetsGroup<MoonbeamHelper>;2760 xTokens: XTokensGroup<MoonbeamHelper>;2761 democracy: MoonbeamDemocracyGroup;2762 collective: {2763 council: MoonbeamCollectiveGroup,2764 techCommittee: MoonbeamCollectiveGroup,2765 };27662767 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2768 super(logger, options.helperBase ?? MoonbeamHelper);27692770 this.balance = new EthereumBalanceGroup(this);2771 this.assetManager = new MoonbeamAssetManagerGroup(this);2772 this.assets = new AssetsGroup(this);2773 this.xTokens = new XTokensGroup(this);2774 this.democracy = new MoonbeamDemocracyGroup(this);2775 this.collective = {2776 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2777 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2778 };2779 }2780}27812782export class AcalaHelper extends XcmChainHelper {2783 balance: SubstrateBalanceGroup<AcalaHelper>;2784 assetRegistry: AcalaAssetRegistryGroup;2785 xTokens: XTokensGroup<AcalaHelper>;2786 tokens: TokensGroup<AcalaHelper>;27872788 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2789 super(logger, options.helperBase ?? AcalaHelper);27902791 this.balance = new SubstrateBalanceGroup(this);2792 this.assetRegistry = new AcalaAssetRegistryGroup(this);2793 this.xTokens = new XTokensGroup(this);2794 this.tokens = new TokensGroup(this);2795 }27962797 getSudo<T extends AcalaHelper>() {2798 // eslint-disable-next-line @typescript-eslint/naming-convention2799 const SudoHelperType = SudoHelper(this.helperBase);2800 return this.clone(SudoHelperType) as T;2801 }2802}28032804// eslint-disable-next-line @typescript-eslint/naming-convention2805function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2806 return class extends Base {2807 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter';2808 scheduledId: string;2809 blocksNum: number;2810 options: ISchedulerOptions;28112812 constructor(...args: any[]) {2813 const logger = args[0] as ILogger;2814 const options = args[1] as {2815 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter',2816 scheduledId: string,2817 blocksNum: number,2818 options: ISchedulerOptions2819 };28202821 super(logger);28222823 this.scheduleFn = options.scheduleFn;2824 this.scheduledId = options.scheduledId;2825 this.blocksNum = options.blocksNum;2826 this.options = options.options;2827 }28282829 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {2830 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);2831 const extrinsic = 'api.tx.scheduler.' + this.scheduleFn;28322833 return super.executeExtrinsic(2834 sender,2835 extrinsic,2836 [2837 this.scheduledId,2838 this.blocksNum,2839 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,2840 this.options.priority ?? null,2841 scheduledTx,2842 ],2843 expectSuccess,2844 );2845 }2846 };2847}28482849// eslint-disable-next-line @typescript-eslint/naming-convention2850function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {2851 return class extends Base {2852 constructor(...args: any[]) {2853 super(...args);2854 }28552856 executeExtrinsic (2857 sender: IKeyringPair,2858 extrinsic: string,2859 params: any[],2860 expectSuccess?: boolean,2861 ): Promise<ITransactionResult> {2862 const call = this.constructApiCall(extrinsic, params);2863 return super.executeExtrinsic(2864 sender,2865 'api.tx.sudo.sudo',2866 [call],2867 expectSuccess,2868 );2869 }2870 };2871}28722873export class UniqueBaseCollection {2874 helper: UniqueHelper;2875 collectionId: number;28762877 constructor(collectionId: number, uniqueHelper: UniqueHelper) {2878 this.collectionId = collectionId;2879 this.helper = uniqueHelper;2880 }28812882 async getData() {2883 return await this.helper.collection.getData(this.collectionId);2884 }28852886 async getLastTokenId() {2887 return await this.helper.collection.getLastTokenId(this.collectionId);2888 }28892890 async doesTokenExist(tokenId: number) {2891 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);2892 }28932894 async getAdmins() {2895 return await this.helper.collection.getAdmins(this.collectionId);2896 }28972898 async getAllowList() {2899 return await this.helper.collection.getAllowList(this.collectionId);2900 }29012902 async getEffectiveLimits() {2903 return await this.helper.collection.getEffectiveLimits(this.collectionId);2904 }29052906 async getProperties(propertyKeys?: string[] | null) {2907 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);2908 }29092910 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {2911 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);2912 }29132914 async getOptions() {2915 return await this.helper.collection.getCollectionOptions(this.collectionId);2916 }29172918 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {2919 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);2920 }29212922 async confirmSponsorship(signer: TSigner) {2923 return await this.helper.collection.confirmSponsorship(signer, this.collectionId);2924 }29252926 async removeSponsor(signer: TSigner) {2927 return await this.helper.collection.removeSponsor(signer, this.collectionId);2928 }29292930 async setLimits(signer: TSigner, limits: ICollectionLimits) {2931 return await this.helper.collection.setLimits(signer, this.collectionId, limits);2932 }29332934 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {2935 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);2936 }29372938 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {2939 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);2940 }29412942 async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {2943 return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);2944 }29452946 async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {2947 return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);2948 }29492950 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {2951 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);2952 }29532954 async setProperties(signer: TSigner, properties: IProperty[]) {2955 return await this.helper.collection.setProperties(signer, this.collectionId, properties);2956 }29572958 async deleteProperties(signer: TSigner, propertyKeys: string[]) {2959 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);2960 }29612962 async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {2963 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);2964 }29652966 async enableNesting(signer: TSigner, permissions: INestingPermissions) {2967 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);2968 }29692970 async disableNesting(signer: TSigner) {2971 return await this.helper.collection.disableNesting(signer, this.collectionId);2972 }29732974 async burn(signer: TSigner) {2975 return await this.helper.collection.burn(signer, this.collectionId);2976 }29772978 scheduleAt<T extends UniqueHelper>(2979 scheduledId: string,2980 executionBlockNumber: number,2981 options: ISchedulerOptions = {},2982 ) {2983 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);2984 return new UniqueBaseCollection(this.collectionId, scheduledHelper);2985 }29862987 scheduleAfter<T extends UniqueHelper>(2988 scheduledId: string,2989 blocksBeforeExecution: number,2990 options: ISchedulerOptions = {},2991 ) {2992 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);2993 return new UniqueBaseCollection(this.collectionId, scheduledHelper);2994 }29952996 getSudo<T extends UniqueHelper>() {2997 return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());2998 }2999}300030013002export class UniqueNFTCollection extends UniqueBaseCollection {3003 getTokenObject(tokenId: number) {3004 return new UniqueNFToken(tokenId, this);3005 }30063007 async getTokensByAddress(addressObj: ICrossAccountId) {3008 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3009 }30103011 async getToken(tokenId: number, blockHashAt?: string) {3012 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3013 }30143015 async getTokenOwner(tokenId: number, blockHashAt?: string) {3016 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3017 }30183019 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3020 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3021 }30223023 async getTokenChildren(tokenId: number, blockHashAt?: string) {3024 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3025 }30263027 async getPropertyPermissions(propertyKeys: string[] | null = null) {3028 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3029 }30303031 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3032 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3033 }30343035 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3036 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3037 }30383039 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3040 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3041 }30423043 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3044 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3045 }30463047 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3048 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3049 }30503051 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3052 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3053 }30543055 async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3056 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3057 }30583059 async burnToken(signer: TSigner, tokenId: number) {3060 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3061 }30623063 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3064 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3065 }30663067 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3068 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3069 }30703071 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3072 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3073 }30743075 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3076 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3077 }30783079 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3080 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3081 }30823083 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3084 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3085 }30863087 scheduleAt<T extends UniqueHelper>(3088 scheduledId: string,3089 executionBlockNumber: number,3090 options: ISchedulerOptions = {},3091 ) {3092 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3093 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3094 }30953096 scheduleAfter<T extends UniqueHelper>(3097 scheduledId: string,3098 blocksBeforeExecution: number,3099 options: ISchedulerOptions = {},3100 ) {3101 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3102 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3103 }31043105 getSudo<T extends UniqueHelper>() {3106 return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3107 }3108}310931103111export class UniqueRFTCollection extends UniqueBaseCollection {3112 getTokenObject(tokenId: number) {3113 return new UniqueRFToken(tokenId, this);3114 }31153116 async getToken(tokenId: number, blockHashAt?: string) {3117 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3118 }31193120 async getTokensByAddress(addressObj: ICrossAccountId) {3121 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3122 }31233124 async getTop10TokenOwners(tokenId: number) {3125 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3126 }31273128 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3129 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3130 }31313132 async getTokenTotalPieces(tokenId: number) {3133 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3134 }31353136 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3137 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3138 }31393140 async getPropertyPermissions(propertyKeys: string[] | null = null) {3141 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3142 }31433144 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3145 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3146 }31473148 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3149 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3150 }31513152 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3153 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3154 }31553156 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3157 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3158 }31593160 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3161 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3162 }31633164 async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3165 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3166 }31673168 async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3169 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3170 }31713172 async burnToken(signer: TSigner, tokenId: number, amount=1n) {3173 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3174 }31753176 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n) {3177 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3178 }31793180 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3181 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3182 }31833184 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3185 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3186 }31873188 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3189 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3190 }31913192 scheduleAt<T extends UniqueHelper>(3193 scheduledId: string,3194 executionBlockNumber: number,3195 options: ISchedulerOptions = {},3196 ) {3197 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3198 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3199 }32003201 scheduleAfter<T extends UniqueHelper>(3202 scheduledId: string,3203 blocksBeforeExecution: number,3204 options: ISchedulerOptions = {},3205 ) {3206 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3207 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3208 }32093210 getSudo<T extends UniqueHelper>() {3211 return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3212 }3213}321432153216export class UniqueFTCollection extends UniqueBaseCollection {3217 async getBalance(addressObj: ICrossAccountId) {3218 return await this.helper.ft.getBalance(this.collectionId, addressObj);3219 }32203221 async getTotalPieces() {3222 return await this.helper.ft.getTotalPieces(this.collectionId);3223 }32243225 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3226 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3227 }32283229 async getTop10Owners() {3230 return await this.helper.ft.getTop10Owners(this.collectionId);3231 }32323233 async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3234 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3235 }32363237 async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3238 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3239 }32403241 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3242 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3243 }32443245 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3246 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3247 }32483249 async burnTokens(signer: TSigner, amount=1n) {3250 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3251 }32523253 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3254 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3255 }32563257 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3258 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3259 }32603261 scheduleAt<T extends UniqueHelper>(3262 scheduledId: string,3263 executionBlockNumber: number,3264 options: ISchedulerOptions = {},3265 ) {3266 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3267 return new UniqueFTCollection(this.collectionId, scheduledHelper);3268 }32693270 scheduleAfter<T extends UniqueHelper>(3271 scheduledId: string,3272 blocksBeforeExecution: number,3273 options: ISchedulerOptions = {},3274 ) {3275 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3276 return new UniqueFTCollection(this.collectionId, scheduledHelper);3277 }32783279 getSudo<T extends UniqueHelper>() {3280 return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3281 }3282}328332843285export class UniqueBaseToken {3286 collection: UniqueNFTCollection | UniqueRFTCollection;3287 collectionId: number;3288 tokenId: number;32893290 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3291 this.collection = collection;3292 this.collectionId = collection.collectionId;3293 this.tokenId = tokenId;3294 }32953296 async getNextSponsored(addressObj: ICrossAccountId) {3297 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3298 }32993300 async getProperties(propertyKeys?: string[] | null) {3301 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3302 }33033304 async setProperties(signer: TSigner, properties: IProperty[]) {3305 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3306 }33073308 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3309 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3310 }33113312 async doesExist() {3313 return await this.collection.doesTokenExist(this.tokenId);3314 }33153316 nestingAccount() {3317 return this.collection.helper.util.getTokenAccount(this);3318 }33193320 scheduleAt<T extends UniqueHelper>(3321 scheduledId: string,3322 executionBlockNumber: number,3323 options: ISchedulerOptions = {},3324 ) {3325 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3326 return new UniqueBaseToken(this.tokenId, scheduledCollection);3327 }33283329 scheduleAfter<T extends UniqueHelper>(3330 scheduledId: string,3331 blocksBeforeExecution: number,3332 options: ISchedulerOptions = {},3333 ) {3334 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3335 return new UniqueBaseToken(this.tokenId, scheduledCollection);3336 }33373338 getSudo<T extends UniqueHelper>() {3339 return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3340 }3341}334233433344export class UniqueNFToken extends UniqueBaseToken {3345 collection: UniqueNFTCollection;33463347 constructor(tokenId: number, collection: UniqueNFTCollection) {3348 super(tokenId, collection);3349 this.collection = collection;3350 }33513352 async getData(blockHashAt?: string) {3353 return await this.collection.getToken(this.tokenId, blockHashAt);3354 }33553356 async getOwner(blockHashAt?: string) {3357 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3358 }33593360 async getTopmostOwner(blockHashAt?: string) {3361 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3362 }33633364 async getChildren(blockHashAt?: string) {3365 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3366 }33673368 async nest(signer: TSigner, toTokenObj: IToken) {3369 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3370 }33713372 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3373 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3374 }33753376 async transfer(signer: TSigner, addressObj: ICrossAccountId) {3377 return await this.collection.transferToken(signer, this.tokenId, addressObj);3378 }33793380 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3381 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3382 }33833384 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3385 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3386 }33873388 async isApproved(toAddressObj: ICrossAccountId) {3389 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3390 }33913392 async burn(signer: TSigner) {3393 return await this.collection.burnToken(signer, this.tokenId);3394 }33953396 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3397 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3398 }33993400 scheduleAt<T extends UniqueHelper>(3401 scheduledId: string,3402 executionBlockNumber: number,3403 options: ISchedulerOptions = {},3404 ) {3405 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3406 return new UniqueNFToken(this.tokenId, scheduledCollection);3407 }34083409 scheduleAfter<T extends UniqueHelper>(3410 scheduledId: string,3411 blocksBeforeExecution: number,3412 options: ISchedulerOptions = {},3413 ) {3414 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3415 return new UniqueNFToken(this.tokenId, scheduledCollection);3416 }34173418 getSudo<T extends UniqueHelper>() {3419 return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3420 }3421}34223423export class UniqueRFToken extends UniqueBaseToken {3424 collection: UniqueRFTCollection;34253426 constructor(tokenId: number, collection: UniqueRFTCollection) {3427 super(tokenId, collection);3428 this.collection = collection;3429 }34303431 async getData(blockHashAt?: string) {3432 return await this.collection.getToken(this.tokenId, blockHashAt);3433 }34343435 async getTop10Owners() {3436 return await this.collection.getTop10TokenOwners(this.tokenId);3437 }34383439 async getBalance(addressObj: ICrossAccountId) {3440 return await this.collection.getTokenBalance(this.tokenId, addressObj);3441 }34423443 async getTotalPieces() {3444 return await this.collection.getTokenTotalPieces(this.tokenId);3445 }34463447 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3448 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3449 }34503451 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3452 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3453 }34543455 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3456 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3457 }34583459 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3460 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3461 }34623463 async repartition(signer: TSigner, amount: bigint) {3464 return await this.collection.repartitionToken(signer, this.tokenId, amount);3465 }34663467 async burn(signer: TSigner, amount=1n) {3468 return await this.collection.burnToken(signer, this.tokenId, amount);3469 }34703471 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3472 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3473 }34743475 scheduleAt<T extends UniqueHelper>(3476 scheduledId: string,3477 executionBlockNumber: number,3478 options: ISchedulerOptions = {},3479 ) {3480 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3481 return new UniqueRFToken(this.tokenId, scheduledCollection);3482 }34833484 scheduleAfter<T extends UniqueHelper>(3485 scheduledId: string,3486 blocksBeforeExecution: number,3487 options: ISchedulerOptions = {},3488 ) {3489 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3490 return new UniqueRFToken(this.tokenId, scheduledCollection);3491 }34923493 getSudo<T extends UniqueHelper>() {3494 return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3495 }3496}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034/* eslint-disable @typescript-eslint/no-var-requires */5/* eslint-disable function-call-argument-newline */6/* eslint-disable no-prototype-builtins */78import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';12import {13 IApiListeners,14 IBlock,15 IEvent,16 IChainProperties,17 ICollectionCreationOptions,18 ICollectionLimits,19 ICollectionPermissions,20 ICrossAccountId,21 ICrossAccountIdLower,22 ILogger,23 INestingPermissions,24 IProperty,25 IStakingInfo,26 ISchedulerOptions,27 ISubstrateBalance,28 IToken,29 ITokenPropertyPermission,30 ITransactionResult,31 IUniqueHelperLog,32 TApiAllowedListeners,33 TEthereumAccount,34 TSigner,35 TSubstrateAccount,36 TNetworks,37 IForeignAssetMetadata,38 AcalaAssetMetadata,39 MoonbeamAssetInfo,40 DemocracyStandardAccountVote,41} from './types';42import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';4344export class CrossAccountId implements ICrossAccountId {45 Substrate?: TSubstrateAccount;46 Ethereum?: TEthereumAccount;4748 constructor(account: ICrossAccountId) {49 if (account.Substrate) this.Substrate = account.Substrate;50 if (account.Ethereum) this.Ethereum = account.Ethereum;51 }5253 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {54 switch (domain) {55 case 'Substrate': return new CrossAccountId({Substrate: account.address});56 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();57 }58 }5960 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {61 return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});62 }6364 static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {65 return encodeAddress(decodeAddress(address), ss58Format);66 }6768 static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {69 return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});70 }7172 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {73 if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);74 return this;75 }7677 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {78 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));79 }8081 toEthereum(): CrossAccountId {82 if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});83 return this;84 }8586 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {87 return evmToAddress(address, ss58Format);88 }8990 toSubstrate(ss58Format?: number): CrossAccountId {91 if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});92 return this;93 }9495 toLowerCase(): CrossAccountId {96 if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();97 if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();98 return this;99 }100}101102const nesting = {103 toChecksumAddress(address: string): string {104 if (typeof address === 'undefined') return '';105106 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);107108 address = address.toLowerCase().replace(/^0x/i,'');109 const addressHash = keccakAsHex(address).replace(/^0x/i,'');110 const checksumAddress = ['0x'];111112 for (let i = 0; i < address.length; i++) {113 // If ith character is 8 to f then make it uppercase114 if (parseInt(addressHash[i], 16) > 7) {115 checksumAddress.push(address[i].toUpperCase());116 } else {117 checksumAddress.push(address[i]);118 }119 }120 return checksumAddress.join('');121 },122 tokenIdToAddress(collectionId: number, tokenId: number) {123 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8,'0')}${tokenId.toString(16).padStart(8,'0')}`);124 },125};126127class UniqueUtil {128 static transactionStatus = {129 NOT_READY: 'NotReady',130 FAIL: 'Fail',131 SUCCESS: 'Success',132 };133134 static chainLogType = {135 EXTRINSIC: 'extrinsic',136 RPC: 'rpc',137 };138139 static getTokenAccount(token: IToken): CrossAccountId {140 return new CrossAccountId({Ethereum: this.getTokenAddress(token)});141 }142143 static getTokenAddress(token: IToken): string {144 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);145 }146147 static getDefaultLogger(): ILogger {148 return {149 log(msg: any, level = 'INFO') {150 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));151 },152 level: {153 ERROR: 'ERROR',154 WARNING: 'WARNING',155 INFO: 'INFO',156 },157 };158 }159160 static vec2str(arr: string[] | number[]) {161 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');162 }163164 static str2vec(string: string) {165 if (typeof string !== 'string') return string;166 return Array.from(string).map(x => x.charCodeAt(0));167 }168169 static fromSeed(seed: string, ss58Format = 42) {170 const keyring = new Keyring({type: 'sr25519', ss58Format});171 return keyring.addFromUri(seed);172 }173174 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {175 if (creationResult.status !== this.transactionStatus.SUCCESS) {176 throw Error('Unable to create collection!');177 }178179 let collectionId = null;180 creationResult.result.events.forEach(({event: {data, method, section}}) => {181 if ((section === 'common') && (method === 'CollectionCreated')) {182 collectionId = parseInt(data[0].toString(), 10);183 }184 });185186 if (collectionId === null) {187 throw Error('No CollectionCreated event was found!');188 }189190 return collectionId;191 }192193 static extractTokensFromCreationResult(creationResult: ITransactionResult): {194 success: boolean,195 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],196 } {197 if (creationResult.status !== this.transactionStatus.SUCCESS) {198 throw Error('Unable to create tokens!');199 }200 let success = false;201 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];202 creationResult.result.events.forEach(({event: {data, method, section}}) => {203 if (method === 'ExtrinsicSuccess') {204 success = true;205 } else if ((section === 'common') && (method === 'ItemCreated')) {206 tokens.push({207 collectionId: parseInt(data[0].toString(), 10),208 tokenId: parseInt(data[1].toString(), 10),209 owner: data[2].toHuman(),210 amount: data[3].toBigInt(),211 });212 }213 });214 return {success, tokens};215 }216217 static extractTokensFromBurnResult(burnResult: ITransactionResult): {218 success: boolean,219 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],220 } {221 if (burnResult.status !== this.transactionStatus.SUCCESS) {222 throw Error('Unable to burn tokens!');223 }224 let success = false;225 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];226 burnResult.result.events.forEach(({event: {data, method, section}}) => {227 if (method === 'ExtrinsicSuccess') {228 success = true;229 } else if ((section === 'common') && (method === 'ItemDestroyed')) {230 tokens.push({231 collectionId: parseInt(data[0].toString(), 10),232 tokenId: parseInt(data[1].toString(), 10),233 owner: data[2].toHuman(),234 amount: data[3].toBigInt(),235 });236 }237 });238 return {success, tokens};239 }240241 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {242 let eventId = null;243 events.forEach(({event: {data, method, section}}) => {244 if ((section === expectedSection) && (method === expectedMethod)) {245 eventId = parseInt(data[0].toString(), 10);246 }247 });248249 if (eventId === null) {250 throw Error(`No ${expectedMethod} event was found!`);251 }252 return eventId === collectionId;253 }254255 static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {256 const normalizeAddress = (address: string | ICrossAccountId) => {257 if(typeof address === 'string') return address;258 const obj = {} as any;259 Object.keys(address).forEach(k => {260 obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];261 });262 if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);263 if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();264 return address;265 };266 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;267 events.forEach(({event: {data, method, section}}) => {268 if ((section === 'common') && (method === 'Transfer')) {269 const hData = (data as any).toJSON();270 transfer = {271 collectionId: hData[0],272 tokenId: hData[1],273 from: normalizeAddress(hData[2]),274 to: normalizeAddress(hData[3]),275 amount: BigInt(hData[4]),276 };277 }278 });279 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;280 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);281 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);282 isSuccess = isSuccess && amount === transfer.amount;283 return isSuccess;284 }285286 static bigIntToDecimals(number: bigint, decimals = 18) {287 const numberStr = number.toString();288 const dotPos = numberStr.length - decimals;289290 if (dotPos <= 0) {291 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;292 } else {293 const intPart = numberStr.substring(0, dotPos);294 const fractPart = numberStr.substring(dotPos);295 return intPart + '.' + fractPart;296 }297 }298}299300class UniqueEventHelper {301 private static extractIndex(index: any): [number, number] | string {302 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];303 return index.toJSON();304 }305306 private static extractSub(data: any, subTypes: any): {[key: string]: any} {307 let obj: any = {};308 let index = 0;309310 if (data.entries) {311 for(const [key, value] of data.entries()) {312 obj[key] = this.extractData(value, subTypes[index]);313 index++;314 }315 } else obj = data.toJSON();316317 return obj;318 }319320 private static extractData(data: any, type: any): any {321 if(!type) return data.toHuman();322 if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();323 if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();324 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);325 return data.toHuman();326 }327328 public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {329 const parsedEvents: IEvent[] = [];330331 events.forEach((record) => {332 const {event, phase} = record;333 const types = event.typeDef;334335 const eventData: IEvent = {336 section: event.section.toString(),337 method: event.method.toString(),338 index: this.extractIndex(event.index),339 data: [],340 phase: phase.toJSON(),341 };342343 event.data.forEach((val: any, index: number) => {344 eventData.data.push(this.extractData(val, types[index]));345 });346347 parsedEvents.push(eventData);348 });349350 return parsedEvents;351 }352}353354export class ChainHelperBase {355 helperBase: any;356357 transactionStatus = UniqueUtil.transactionStatus;358 chainLogType = UniqueUtil.chainLogType;359 util: typeof UniqueUtil;360 eventHelper: typeof UniqueEventHelper;361 logger: ILogger;362 api: ApiPromise | null;363 forcedNetwork: TNetworks | null;364 network: TNetworks | null;365 chainLog: IUniqueHelperLog[];366 children: ChainHelperBase[];367 address: AddressGroup;368 chain: ChainGroup;369370 constructor(logger?: ILogger, helperBase?: any) {371 this.helperBase = helperBase;372373 this.util = UniqueUtil;374 this.eventHelper = UniqueEventHelper;375 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();376 this.logger = logger;377 this.api = null;378 this.forcedNetwork = null;379 this.network = null;380 this.chainLog = [];381 this.children = [];382 this.address = new AddressGroup(this);383 this.chain = new ChainGroup(this);384 }385386 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {387 Object.setPrototypeOf(helperCls.prototype, this);388 const newHelper = new helperCls(this.logger, options);389390 newHelper.api = this.api;391 newHelper.network = this.network;392 newHelper.forceNetwork = this.forceNetwork;393394 this.children.push(newHelper);395396 return newHelper;397 }398399 getApi(): ApiPromise {400 if(this.api === null) throw Error('API not initialized');401 return this.api;402 }403404 clearChainLog(): void {405 this.chainLog = [];406 }407408 forceNetwork(value: TNetworks): void {409 this.forcedNetwork = value;410 }411412 async connect(wsEndpoint: string, listeners?: IApiListeners) {413 if (this.api !== null) throw Error('Already connected');414 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);415 this.api = api;416 this.network = network;417 }418419 async disconnect() {420 for (const child of this.children) {421 child.clearApi();422 }423424 if (this.api === null) return;425 await this.api.disconnect();426 this.clearApi();427 }428429 clearApi() {430 this.api = null;431 this.network = null;432 }433434 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {435 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;436 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];437438 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;439440 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;441 return 'opal';442 }443444 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {445 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});446 await api.isReady;447448 const network = await this.detectNetwork(api);449450 await api.disconnect();451452 return network;453 }454455 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{456 api: ApiPromise;457 network: TNetworks;458 }> {459 if(typeof network === 'undefined' || network === null) network = 'opal';460 const supportedRPC = {461 opal: {462 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,463 },464 quartz: {465 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,466 },467 unique: {468 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,469 },470 rococo: {},471 westend: {},472 moonbeam: {},473 moonriver: {},474 acala: {},475 karura: {},476 westmint: {},477 };478 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);479 const rpc = supportedRPC[network];480481 // TODO: investigate how to replace rpc in runtime482 // api._rpcCore.addUserInterfaces(rpc);483484 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});485486 await api.isReadyOrError;487488 if (typeof listeners === 'undefined') listeners = {};489 for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {490 if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;491 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);492 }493494 return {api, network};495 }496497 getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {498 const {events, status} = data;499 if (status.isReady) {500 return this.transactionStatus.NOT_READY;501 }502 if (status.isBroadcast) {503 return this.transactionStatus.NOT_READY;504 }505 if (status.isInBlock || status.isFinalized) {506 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');507 if (errors.length > 0) {508 return this.transactionStatus.FAIL;509 }510 if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {511 return this.transactionStatus.SUCCESS;512 }513 }514515 return this.transactionStatus.FAIL;516 }517518 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {519 const sign = (callback: any) => {520 if(options !== null) return transaction.signAndSend(sender, options, callback);521 return transaction.signAndSend(sender, callback);522 };523 // eslint-disable-next-line no-async-promise-executor524 return new Promise(async (resolve, reject) => {525 try {526 const unsub = await sign((result: any) => {527 const status = this.getTransactionStatus(result);528529 if (status === this.transactionStatus.SUCCESS) {530 this.logger.log(`${label} successful`);531 unsub();532 resolve({result, status});533 } else if (status === this.transactionStatus.FAIL) {534 let moduleError = null;535536 if (result.hasOwnProperty('dispatchError')) {537 const dispatchError = result['dispatchError'];538539 if (dispatchError) {540 if (dispatchError.isModule) {541 const modErr = dispatchError.asModule;542 const errorMeta = dispatchError.registry.findMetaError(modErr);543544 moduleError = `${errorMeta.section}.${errorMeta.name}`;545 } else {546 moduleError = dispatchError.toHuman();547 }548 } else {549 this.logger.log(result, this.logger.level.ERROR);550 }551 }552553 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);554 unsub();555 reject({status, moduleError, result});556 }557 });558 } catch (e) {559 this.logger.log(e, this.logger.level.ERROR);560 reject(e);561 }562 });563 }564565 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {566 const api = this.getApi();567 const signingInfo = await api.derive.tx.signingInfo(signer.address);568569 // We need to sign the tx because570 // unsigned transactions does not have an inclusion fee571 tx.sign(signer, {572 blockHash: api.genesisHash,573 genesisHash: api.genesisHash,574 runtimeVersion: api.runtimeVersion,575 nonce: signingInfo.nonce,576 });577578 if (len === null) {579 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;580 } else {581 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;582 }583 }584585 constructApiCall(apiCall: string, params: any[]) {586 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);587 let call = this.getApi() as any;588 for(const part of apiCall.slice(4).split('.')) {589 call = call[part];590 }591 return call(...params);592 }593594 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {595 if(this.api === null) throw Error('API not initialized');596 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);597598 const startTime = (new Date()).getTime();599 let result: ITransactionResult;600 let events: IEvent[] = [];601 try {602 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;603 events = this.eventHelper.extractEvents(result.result.events);604 }605 catch(e) {606 if(!(e as object).hasOwnProperty('status')) throw e;607 result = e as ITransactionResult;608 }609610 const endTime = (new Date()).getTime();611612 const log = {613 executedAt: endTime,614 executionTime: endTime - startTime,615 type: this.chainLogType.EXTRINSIC,616 status: result.status,617 call: extrinsic,618 signer: this.getSignerAddress(sender),619 params,620 } as IUniqueHelperLog;621622 if(result.status !== this.transactionStatus.SUCCESS) {623 if (result.moduleError) log.moduleError = result.moduleError;624 else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;625 }626 if(events.length > 0) log.events = events;627628 this.chainLog.push(log);629630 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {631 if (result.moduleError) throw Error(`${result.moduleError}`);632 else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));633 }634 return result;635 }636637 async callRpc(rpc: string, params?: any[]) {638 if(typeof params === 'undefined') params = [];639 if(this.api === null) throw Error('API not initialized');640 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);641642 const startTime = (new Date()).getTime();643 let result;644 let error = null;645 const log = {646 type: this.chainLogType.RPC,647 call: rpc,648 params,649 } as IUniqueHelperLog;650651 try {652 result = await this.constructApiCall(rpc, params);653 }654 catch(e) {655 error = e;656 }657658 const endTime = (new Date()).getTime();659660 log.executedAt = endTime;661 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';662 log.executionTime = endTime - startTime;663664 this.chainLog.push(log);665666 if(error !== null) throw error;667668 return result;669 }670671 getSignerAddress(signer: IKeyringPair | string): string {672 if(typeof signer === 'string') return signer;673 return signer.address;674 }675676 fetchAllPalletNames(): string[] {677 if(this.api === null) throw Error('API not initialized');678 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());679 }680681 fetchMissingPalletNames(requiredPallets: string[]): string[] {682 const palletNames = this.fetchAllPalletNames();683 return requiredPallets.filter(p => !palletNames.includes(p));684 }685}686687688class HelperGroup<T extends ChainHelperBase> {689 helper: T;690691 constructor(uniqueHelper: T) {692 this.helper = uniqueHelper;693 }694}695696697class CollectionGroup extends HelperGroup<UniqueHelper> {698 /**699 * Get number of blocks when sponsored transaction is available.700 *701 * @param collectionId ID of collection702 * @param tokenId ID of token703 * @param addressObj address for which the sponsorship is checked704 * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});705 * @returns number of blocks or null if sponsorship hasn't been set706 */707 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {708 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();709 }710711 /**712 * Get the number of created collections.713 *714 * @returns number of created collections715 */716 async getTotalCount(): Promise<number> {717 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();718 }719720 /**721 * Get information about the collection with additional data,722 * including the number of tokens it contains, its administrators,723 * the normalized address of the collection's owner, and decoded name and description.724 *725 * @param collectionId ID of collection726 * @example await getData(2)727 * @returns collection information object728 */729 async getData(collectionId: number): Promise<{730 id: number;731 name: string;732 description: string;733 tokensCount: number;734 admins: CrossAccountId[];735 normalizedOwner: TSubstrateAccount;736 raw: any737 } | null> {738 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);739 const humanCollection = collection.toHuman(), collectionData = {740 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],741 raw: humanCollection,742 } as any, jsonCollection = collection.toJSON();743 if (humanCollection === null) return null;744 collectionData.raw.limits = jsonCollection.limits;745 collectionData.raw.permissions = jsonCollection.permissions;746 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);747 for (const key of ['name', 'description']) {748 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);749 }750751 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))752 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)753 : 0;754 collectionData.admins = await this.getAdmins(collectionId);755756 return collectionData;757 }758759 /**760 * Get the addresses of the collection's administrators, optionally normalized.761 *762 * @param collectionId ID of collection763 * @param normalize whether to normalize the addresses to the default ss58 format764 * @example await getAdmins(1)765 * @returns array of administrators766 */767 async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {768 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();769770 return normalize771 ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())772 : admins;773 }774775 /**776 * Get the addresses added to the collection allow-list, optionally normalized.777 * @param collectionId ID of collection778 * @param normalize whether to normalize the addresses to the default ss58 format779 * @example await getAllowList(1)780 * @returns array of allow-listed addresses781 */782 async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {783 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();784 return normalize785 ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())786 : allowListed;787 }788789 /**790 * Get the effective limits of the collection instead of null for default values791 *792 * @param collectionId ID of collection793 * @example await getEffectiveLimits(2)794 * @returns object of collection limits795 */796 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {797 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();798 }799800 /**801 * Burns the collection if the signer has sufficient permissions and collection is empty.802 *803 * @param signer keyring of signer804 * @param collectionId ID of collection805 * @example await helper.collection.burn(aliceKeyring, 3);806 * @returns ```true``` if extrinsic success, otherwise ```false```807 */808 async burn(signer: TSigner, collectionId: number): Promise<boolean> {809 const result = await this.helper.executeExtrinsic(810 signer,811 'api.tx.unique.destroyCollection', [collectionId],812 true,813 );814815 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');816 }817818 /**819 * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.820 *821 * @param signer keyring of signer822 * @param collectionId ID of collection823 * @param sponsorAddress Sponsor substrate address824 * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")825 * @returns ```true``` if extrinsic success, otherwise ```false```826 */827 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {828 const result = await this.helper.executeExtrinsic(829 signer,830 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],831 true,832 );833834 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet');835 }836837 /**838 * Confirms consent to sponsor the collection on behalf of the signer.839 *840 * @param signer keyring of signer841 * @param collectionId ID of collection842 * @example confirmSponsorship(aliceKeyring, 10)843 * @returns ```true``` if extrinsic success, otherwise ```false```844 */845 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {846 const result = await this.helper.executeExtrinsic(847 signer,848 'api.tx.unique.confirmSponsorship', [collectionId],849 true,850 );851852 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed');853 }854855 /**856 * Removes the sponsor of a collection, regardless if it consented or not.857 *858 * @param signer keyring of signer859 * @param collectionId ID of collection860 * @example removeSponsor(aliceKeyring, 10)861 * @returns ```true``` if extrinsic success, otherwise ```false```862 */863 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {864 const result = await this.helper.executeExtrinsic(865 signer,866 'api.tx.unique.removeCollectionSponsor', [collectionId],867 true,868 );869870 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorRemoved');871 }872873 /**874 * Sets the limits of the collection. At least one limit must be specified for a correct call.875 *876 * @param signer keyring of signer877 * @param collectionId ID of collection878 * @param limits collection limits object879 * @example880 * await setLimits(881 * aliceKeyring,882 * 10,883 * {884 * sponsorTransferTimeout: 0,885 * ownerCanDestroy: false886 * }887 * )888 * @returns ```true``` if extrinsic success, otherwise ```false```889 */890 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {891 const result = await this.helper.executeExtrinsic(892 signer,893 'api.tx.unique.setCollectionLimits', [collectionId, limits],894 true,895 );896897 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet');898 }899900 /**901 * Changes the owner of the collection to the new Substrate address.902 *903 * @param signer keyring of signer904 * @param collectionId ID of collection905 * @param ownerAddress substrate address of new owner906 * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")907 * @returns ```true``` if extrinsic success, otherwise ```false```908 */909 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {910 const result = await this.helper.executeExtrinsic(911 signer,912 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],913 true,914 );915916 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged');917 }918919 /**920 * Adds a collection administrator.921 *922 * @param signer keyring of signer923 * @param collectionId ID of collection924 * @param adminAddressObj Administrator address (substrate or ethereum)925 * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})926 * @returns ```true``` if extrinsic success, otherwise ```false```927 */928 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {929 const result = await this.helper.executeExtrinsic(930 signer,931 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],932 true,933 );934935 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded');936 }937938 /**939 * Removes a collection administrator.940 *941 * @param signer keyring of signer942 * @param collectionId ID of collection943 * @param adminAddressObj Administrator address (substrate or ethereum)944 * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})945 * @returns ```true``` if extrinsic success, otherwise ```false```946 */947 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {948 const result = await this.helper.executeExtrinsic(949 signer,950 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],951 true,952 );953954 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved');955 }956957 /**958 * Check if user is in allow list.959 *960 * @param collectionId ID of collection961 * @param user Account to check962 * @example await getAdmins(1)963 * @returns is user in allow list964 */965 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {966 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();967 }968969 /**970 * Adds an address to allow list971 * @param signer keyring of signer972 * @param collectionId ID of collection973 * @param addressObj address to add to the allow list974 * @returns ```true``` if extrinsic success, otherwise ```false```975 */976 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {977 const result = await this.helper.executeExtrinsic(978 signer,979 'api.tx.unique.addToAllowList', [collectionId, addressObj],980 true,981 );982983 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressAdded');984 }985986 /**987 * Removes an address from allow list988 *989 * @param signer keyring of signer990 * @param collectionId ID of collection991 * @param addressObj address to remove from the allow list992 * @returns ```true``` if extrinsic success, otherwise ```false```993 */994 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {995 const result = await this.helper.executeExtrinsic(996 signer,997 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],998 true,999 );10001001 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved');1002 }10031004 /**1005 * Sets onchain permissions for selected collection.1006 *1007 * @param signer keyring of signer1008 * @param collectionId ID of collection1009 * @param permissions collection permissions object1010 * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});1011 * @returns ```true``` if extrinsic success, otherwise ```false```1012 */1013 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1014 const result = await this.helper.executeExtrinsic(1015 signer,1016 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1017 true,1018 );10191020 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet');1021 }10221023 /**1024 * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.1025 *1026 * @param signer keyring of signer1027 * @param collectionId ID of collection1028 * @param permissions nesting permissions object1029 * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});1030 * @returns ```true``` if extrinsic success, otherwise ```false```1031 */1032 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1033 return await this.setPermissions(signer, collectionId, {nesting: permissions});1034 }10351036 /**1037 * Disables nesting for selected collection.1038 *1039 * @param signer keyring of signer1040 * @param collectionId ID of collection1041 * @example disableNesting(aliceKeyring, 10);1042 * @returns ```true``` if extrinsic success, otherwise ```false```1043 */1044 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1045 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1046 }10471048 /**1049 * Sets onchain properties to the collection.1050 *1051 * @param signer keyring of signer1052 * @param collectionId ID of collection1053 * @param properties array of property objects1054 * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);1055 * @returns ```true``` if extrinsic success, otherwise ```false```1056 */1057 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1058 const result = await this.helper.executeExtrinsic(1059 signer,1060 'api.tx.unique.setCollectionProperties', [collectionId, properties],1061 true,1062 );10631064 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1065 }10661067 /**1068 * Get collection properties.1069 *1070 * @param collectionId ID of collection1071 * @param propertyKeys optionally filter the returned properties to only these keys1072 * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);1073 * @returns array of key-value pairs1074 */1075 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1076 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1077 }10781079 async getCollectionOptions(collectionId: number) {1080 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1081 }10821083 /**1084 * Deletes onchain properties from the collection.1085 *1086 * @param signer keyring of signer1087 * @param collectionId ID of collection1088 * @param propertyKeys array of property keys to delete1089 * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);1090 * @returns ```true``` if extrinsic success, otherwise ```false```1091 */1092 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1093 const result = await this.helper.executeExtrinsic(1094 signer,1095 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1096 true,1097 );10981099 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1100 }11011102 /**1103 * Changes the owner of the token.1104 *1105 * @param signer keyring of signer1106 * @param collectionId ID of collection1107 * @param tokenId ID of token1108 * @param addressObj address of a new owner1109 * @param amount amount of tokens to be transfered. For NFT must be set to 1n1110 * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1111 * @returns true if the token success, otherwise false1112 */1113 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1114 const result = await this.helper.executeExtrinsic(1115 signer,1116 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1117 true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1118 );11191120 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1121 }11221123 /**1124 *1125 * Change ownership of a token(s) on behalf of the owner.1126 *1127 * @param signer keyring of signer1128 * @param collectionId ID of collection1129 * @param tokenId ID of token1130 * @param fromAddressObj address on behalf of which the token will be sent1131 * @param toAddressObj new token owner1132 * @param amount amount of tokens to be transfered. For NFT must be set to 1n1133 * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})1134 * @returns true if the token success, otherwise false1135 */1136 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1137 const result = await this.helper.executeExtrinsic(1138 signer,1139 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1140 true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1141 );1142 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1143 }11441145 /**1146 *1147 * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.1148 *1149 * @param signer keyring of signer1150 * @param collectionId ID of collection1151 * @param tokenId ID of token1152 * @param amount amount of tokens to be burned. For NFT must be set to 1n1153 * @example burnToken(aliceKeyring, 10, 5);1154 * @returns ```true``` if the extrinsic is successful, otherwise ```false```1155 */1156 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1157 const burnResult = await this.helper.executeExtrinsic(1158 signer,1159 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1160 true, // `Unable to burn token for ${label}`,1161 );1162 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1163 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1164 return burnedTokens.success;1165 }11661167 /**1168 * Destroys a concrete instance of NFT on behalf of the owner1169 *1170 * @param signer keyring of signer1171 * @param collectionId ID of collection1172 * @param tokenId ID of token1173 * @param fromAddressObj address on behalf of which the token will be burnt1174 * @param amount amount of tokens to be burned. For NFT must be set to 1n1175 * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})1176 * @returns ```true``` if extrinsic success, otherwise ```false```1177 */1178 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1179 const burnResult = await this.helper.executeExtrinsic(1180 signer,1181 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1182 true, // `Unable to burn token from for ${label}`,1183 );1184 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1185 return burnedTokens.success && burnedTokens.tokens.length > 0;1186 }11871188 /**1189 * Set, change, or remove approved address to transfer the ownership of the NFT.1190 *1191 * @param signer keyring of signer1192 * @param collectionId ID of collection1193 * @param tokenId ID of token1194 * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens1195 * @param amount amount of token to be approved. For NFT must be set to 1n1196 * @returns ```true``` if extrinsic success, otherwise ```false```1197 */1198 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1199 const approveResult = await this.helper.executeExtrinsic(1200 signer,1201 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1202 true, // `Unable to approve token for ${label}`,1203 );12041205 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1206 }12071208 /**1209 * Get the amount of token pieces approved to transfer or burn. Normally 0.1210 *1211 * @param collectionId ID of collection1212 * @param tokenId ID of token1213 * @param toAccountObj address which is approved to use token pieces1214 * @param fromAccountObj address which may have allowed the use of its owned tokens1215 * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})1216 * @returns number of approved to transfer pieces1217 */1218 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1219 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1220 }12211222 /**1223 * Get the last created token ID in a collection1224 *1225 * @param collectionId ID of collection1226 * @example getLastTokenId(10);1227 * @returns id of the last created token1228 */1229 async getLastTokenId(collectionId: number): Promise<number> {1230 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1231 }12321233 /**1234 * Check if token exists1235 *1236 * @param collectionId ID of collection1237 * @param tokenId ID of token1238 * @example doesTokenExist(10, 20);1239 * @returns true if the token exists, otherwise false1240 */1241 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1242 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1243 }1244}12451246class NFTnRFT extends CollectionGroup {1247 /**1248 * Get tokens owned by account1249 *1250 * @param collectionId ID of collection1251 * @param addressObj tokens owner1252 * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})1253 * @returns array of token ids owned by account1254 */1255 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1256 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1257 }12581259 /**1260 * Get token data1261 *1262 * @param collectionId ID of collection1263 * @param tokenId ID of token1264 * @param propertyKeys optionally filter the token properties to only these keys1265 * @param blockHashAt optionally query the data at some block with this hash1266 * @example getToken(10, 5);1267 * @returns human readable token data1268 */1269 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1270 properties: IProperty[];1271 owner: CrossAccountId;1272 normalizedOwner: CrossAccountId;1273 }| null> {1274 let tokenData;1275 if(typeof blockHashAt === 'undefined') {1276 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1277 }1278 else {1279 if(propertyKeys.length == 0) {1280 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1281 if(!collection) return null;1282 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1283 }1284 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1285 }1286 tokenData = tokenData.toHuman();1287 if (tokenData === null || tokenData.owner === null) return null;1288 const owner = {} as any;1289 for (const key of Object.keys(tokenData.owner)) {1290 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1291 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1292 : tokenData.owner[key];1293 }1294 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1295 return tokenData;1296 }12971298 /**1299 * Set permissions to change token properties1300 *1301 * @param signer keyring of signer1302 * @param collectionId ID of collection1303 * @param permissions permissions to change a property by the collection admin or token owner1304 * @example setTokenPropertyPermissions(1305 * aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]1306 * )1307 * @returns true if extrinsic success otherwise false1308 */1309 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1310 const result = await this.helper.executeExtrinsic(1311 signer,1312 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1313 true,1314 );13151316 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1317 }13181319 /**1320 * Get token property permissions.1321 *1322 * @param collectionId ID of collection1323 * @param propertyKeys optionally filter the returned property permissions to only these keys1324 * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);1325 * @returns array of key-permission pairs1326 */1327 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1328 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1329 }13301331 /**1332 * Set token properties1333 *1334 * @param signer keyring of signer1335 * @param collectionId ID of collection1336 * @param tokenId ID of token1337 * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection1338 * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])1339 * @returns ```true``` if extrinsic success, otherwise ```false```1340 */1341 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1342 const result = await this.helper.executeExtrinsic(1343 signer,1344 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1345 true,1346 );13471348 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1349 }13501351 /**1352 * Get properties, metadata assigned to a token.1353 *1354 * @param collectionId ID of collection1355 * @param tokenId ID of token1356 * @param propertyKeys optionally filter the returned properties to only these keys1357 * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);1358 * @returns array of key-value pairs1359 */1360 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1361 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1362 }13631364 /**1365 * Delete the provided properties of a token1366 * @param signer keyring of signer1367 * @param collectionId ID of collection1368 * @param tokenId ID of token1369 * @param propertyKeys property keys to be deleted1370 * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])1371 * @returns ```true``` if extrinsic success, otherwise ```false```1372 */1373 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1374 const result = await this.helper.executeExtrinsic(1375 signer,1376 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1377 true,1378 );13791380 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1381 }13821383 /**1384 * Mint new collection1385 *1386 * @param signer keyring of signer1387 * @param collectionOptions basic collection options and properties1388 * @param mode NFT or RFT type of a collection1389 * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")1390 * @returns object of the created collection1391 */1392 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1393 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1394 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1395 for (const key of ['name', 'description', 'tokenPrefix']) {1396 if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1397 }1398 const creationResult = await this.helper.executeExtrinsic(1399 signer,1400 'api.tx.unique.createCollectionEx', [collectionOptions],1401 true, // errorLabel,1402 );1403 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1404 }14051406 getCollectionObject(_collectionId: number): any {1407 return null;1408 }14091410 getTokenObject(_collectionId: number, _tokenId: number): any {1411 return null;1412 }1413}141414151416class NFTGroup extends NFTnRFT {1417 /**1418 * Get collection object1419 * @param collectionId ID of collection1420 * @example getCollectionObject(2);1421 * @returns instance of UniqueNFTCollection1422 */1423 getCollectionObject(collectionId: number): UniqueNFTCollection {1424 return new UniqueNFTCollection(collectionId, this.helper);1425 }14261427 /**1428 * Get token object1429 * @param collectionId ID of collection1430 * @param tokenId ID of token1431 * @example getTokenObject(10, 5);1432 * @returns instance of UniqueNFTToken1433 */1434 getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1435 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1436 }14371438 /**1439 * Get token's owner1440 * @param collectionId ID of collection1441 * @param tokenId ID of token1442 * @param blockHashAt optionally query the data at the block with this hash1443 * @example getTokenOwner(10, 5);1444 * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}1445 */1446 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1447 let owner;1448 if (typeof blockHashAt === 'undefined') {1449 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1450 } else {1451 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1452 }1453 return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1454 }14551456 /**1457 * Is token approved to transfer1458 * @param collectionId ID of collection1459 * @param tokenId ID of token1460 * @param toAccountObj address to be approved1461 * @returns ```true``` if extrinsic success, otherwise ```false```1462 */1463 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1464 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1465 }14661467 /**1468 * Changes the owner of the token.1469 *1470 * @param signer keyring of signer1471 * @param collectionId ID of collection1472 * @param tokenId ID of token1473 * @param addressObj address of a new owner1474 * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1475 * @returns ```true``` if extrinsic success, otherwise ```false```1476 */1477 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1478 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1479 }14801481 /**1482 *1483 * Change ownership of a NFT on behalf of the owner.1484 *1485 * @param signer keyring of signer1486 * @param collectionId ID of collection1487 * @param tokenId ID of token1488 * @param fromAddressObj address on behalf of which the token will be sent1489 * @param toAddressObj new token owner1490 * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})1491 * @returns ```true``` if extrinsic success, otherwise ```false```1492 */1493 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1494 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1495 }14961497 /**1498 * Recursively find the address that owns the token1499 * @param collectionId ID of collection1500 * @param tokenId ID of token1501 * @param blockHashAt1502 * @example getTokenTopmostOwner(10, 5);1503 * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}1504 */1505 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1506 let owner;1507 if (typeof blockHashAt === 'undefined') {1508 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1509 } else {1510 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1511 }15121513 if (owner === null) return null;15141515 return owner.toHuman();1516 }15171518 /**1519 * Get tokens nested in the provided token1520 * @param collectionId ID of collection1521 * @param tokenId ID of token1522 * @param blockHashAt optionally query the data at the block with this hash1523 * @example getTokenChildren(10, 5);1524 * @returns tokens whose depth of nesting is <= 51525 */1526 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1527 let children;1528 if(typeof blockHashAt === 'undefined') {1529 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1530 } else {1531 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1532 }15331534 return children.toJSON().map((x: any) => {1535 return {collectionId: x.collection, tokenId: x.token};1536 });1537 }15381539 /**1540 * Nest one token into another1541 * @param signer keyring of signer1542 * @param tokenObj token to be nested1543 * @param rootTokenObj token to be parent1544 * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});1545 * @returns ```true``` if extrinsic success, otherwise ```false```1546 */1547 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1548 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1549 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1550 if(!result) {1551 throw Error('Unable to nest token!');1552 }1553 return result;1554 }15551556 /**1557 * Remove token from nested state1558 * @param signer keyring of signer1559 * @param tokenObj token to unnest1560 * @param rootTokenObj parent of a token1561 * @param toAddressObj address of a new token owner1562 * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});1563 * @returns ```true``` if extrinsic success, otherwise ```false```1564 */1565 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1566 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1567 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1568 if(!result) {1569 throw Error('Unable to unnest token!');1570 }1571 return result;1572 }15731574 /**1575 * Mint new collection1576 * @param signer keyring of signer1577 * @param collectionOptions Collection options1578 * @example1579 * mintCollection(aliceKeyring, {1580 * name: 'New',1581 * description: 'New collection',1582 * tokenPrefix: 'NEW',1583 * })1584 * @returns object of the created collection1585 */1586 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1587 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1588 }15891590 /**1591 * Mint new token1592 * @param signer keyring of signer1593 * @param data token data1594 * @returns created token object1595 */1596 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1597 const creationResult = await this.helper.executeExtrinsic(1598 signer,1599 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1600 nft: {1601 properties: data.properties,1602 },1603 }],1604 true,1605 );1606 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1607 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1608 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1609 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1610 }16111612 /**1613 * Mint multiple NFT tokens1614 * @param signer keyring of signer1615 * @param collectionId ID of collection1616 * @param tokens array of tokens with owner and properties1617 * @example1618 * mintMultipleTokens(aliceKeyring, 10, [{1619 * owner: {Substrate: "5DyN4Y92vZCjv38fg..."},1620 * properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],1621 * },{1622 * owner: {Ethereum: "0x9F0583DbB855d..."},1623 * properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],1624 * }]);1625 * @returns ```true``` if extrinsic success, otherwise ```false```1626 */1627 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1628 const creationResult = await this.helper.executeExtrinsic(1629 signer,1630 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1631 true,1632 );1633 const collection = this.getCollectionObject(collectionId);1634 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1635 }16361637 /**1638 * Mint multiple NFT tokens with one owner1639 * @param signer keyring of signer1640 * @param collectionId ID of collection1641 * @param owner tokens owner1642 * @param tokens array of tokens with owner and properties1643 * @example1644 * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{1645 * properties: [{1646 * key: "gender",1647 * value: "female",1648 * },{1649 * key: "age",1650 * value: "33",1651 * }],1652 * }]);1653 * @returns array of newly created tokens1654 */1655 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1656 const rawTokens = [];1657 for (const token of tokens) {1658 const raw = {NFT: {properties: token.properties}};1659 rawTokens.push(raw);1660 }1661 const creationResult = await this.helper.executeExtrinsic(1662 signer,1663 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1664 true,1665 );1666 const collection = this.getCollectionObject(collectionId);1667 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1668 }16691670 /**1671 * Set, change, or remove approved address to transfer the ownership of the NFT.1672 *1673 * @param signer keyring of signer1674 * @param collectionId ID of collection1675 * @param tokenId ID of token1676 * @param toAddressObj address to approve1677 * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1678 * @returns ```true``` if extrinsic success, otherwise ```false```1679 */1680 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1681 return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1682 }1683}168416851686class RFTGroup extends NFTnRFT {1687 /**1688 * Get collection object1689 * @param collectionId ID of collection1690 * @example getCollectionObject(2);1691 * @returns instance of UniqueRFTCollection1692 */1693 getCollectionObject(collectionId: number): UniqueRFTCollection {1694 return new UniqueRFTCollection(collectionId, this.helper);1695 }16961697 /**1698 * Get token object1699 * @param collectionId ID of collection1700 * @param tokenId ID of token1701 * @example getTokenObject(10, 5);1702 * @returns instance of UniqueNFTToken1703 */1704 getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1705 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1706 }17071708 /**1709 * Get top 10 token owners with the largest number of pieces1710 * @param collectionId ID of collection1711 * @param tokenId ID of token1712 * @example getTokenTop10Owners(10, 5);1713 * @returns array of top 10 owners1714 */1715 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1716 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1717 }17181719 /**1720 * Get number of pieces owned by address1721 * @param collectionId ID of collection1722 * @param tokenId ID of token1723 * @param addressObj address token owner1724 * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});1725 * @returns number of pieces ownerd by address1726 */1727 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1728 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1729 }17301731 /**1732 * Transfer pieces of token to another address1733 * @param signer keyring of signer1734 * @param collectionId ID of collection1735 * @param tokenId ID of token1736 * @param addressObj address of a new owner1737 * @param amount number of pieces to be transfered1738 * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)1739 * @returns ```true``` if extrinsic success, otherwise ```false```1740 */1741 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1742 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1743 }17441745 /**1746 * Change ownership of some pieces of RFT on behalf of the owner.1747 * @param signer keyring of signer1748 * @param collectionId ID of collection1749 * @param tokenId ID of token1750 * @param fromAddressObj address on behalf of which the token will be sent1751 * @param toAddressObj new token owner1752 * @param amount number of pieces to be transfered1753 * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)1754 * @returns ```true``` if extrinsic success, otherwise ```false```1755 */1756 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1757 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1758 }17591760 /**1761 * Mint new collection1762 * @param signer keyring of signer1763 * @param collectionOptions Collection options1764 * @example1765 * mintCollection(aliceKeyring, {1766 * name: 'New',1767 * description: 'New collection',1768 * tokenPrefix: 'NEW',1769 * })1770 * @returns object of the created collection1771 */1772 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1773 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1774 }17751776 /**1777 * Mint new token1778 * @param signer keyring of signer1779 * @param data token data1780 * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});1781 * @returns created token object1782 */1783 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1784 const creationResult = await this.helper.executeExtrinsic(1785 signer,1786 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1787 refungible: {1788 pieces: data.pieces,1789 properties: data.properties,1790 },1791 }],1792 true,1793 );1794 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1795 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1796 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1797 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1798 }17991800 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1801 throw Error('Not implemented');1802 const creationResult = await this.helper.executeExtrinsic(1803 signer,1804 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1805 true, // `Unable to mint RFT tokens for ${label}`,1806 );1807 const collection = this.getCollectionObject(collectionId);1808 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1809 }18101811 /**1812 * Mint multiple RFT tokens with one owner1813 * @param signer keyring of signer1814 * @param collectionId ID of collection1815 * @param owner tokens owner1816 * @param tokens array of tokens with properties and pieces1817 * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);1818 * @returns array of newly created RFT tokens1819 */1820 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1821 const rawTokens = [];1822 for (const token of tokens) {1823 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1824 rawTokens.push(raw);1825 }1826 const creationResult = await this.helper.executeExtrinsic(1827 signer,1828 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1829 true,1830 );1831 const collection = this.getCollectionObject(collectionId);1832 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1833 }18341835 /**1836 * Destroys a concrete instance of RFT.1837 * @param signer keyring of signer1838 * @param collectionId ID of collection1839 * @param tokenId ID of token1840 * @param amount number of pieces to be burnt1841 * @example burnToken(aliceKeyring, 10, 5);1842 * @returns ```true``` if the extrinsic is successful, otherwise ```false```1843 */1844 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1845 return await super.burnToken(signer, collectionId, tokenId, amount);1846 }18471848 /**1849 * Destroys a concrete instance of RFT on behalf of the owner.1850 * @param signer keyring of signer1851 * @param collectionId ID of collection1852 * @param tokenId ID of token1853 * @param fromAddressObj address on behalf of which the token will be burnt1854 * @param amount number of pieces to be burnt1855 * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)1856 * @returns ```true``` if extrinsic success, otherwise ```false```1857 */1858 async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1859 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1860 }18611862 /**1863 * Set, change, or remove approved address to transfer the ownership of the RFT.1864 *1865 * @param signer keyring of signer1866 * @param collectionId ID of collection1867 * @param tokenId ID of token1868 * @param toAddressObj address to approve1869 * @param amount number of pieces to be approved1870 * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);1871 * @returns true if the token success, otherwise false1872 */1873 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1874 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1875 }18761877 /**1878 * Get total number of pieces1879 * @param collectionId ID of collection1880 * @param tokenId ID of token1881 * @example getTokenTotalPieces(10, 5);1882 * @returns number of pieces1883 */1884 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1885 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1886 }18871888 /**1889 * Change number of token pieces. Signer must be the owner of all token pieces.1890 * @param signer keyring of signer1891 * @param collectionId ID of collection1892 * @param tokenId ID of token1893 * @param amount new number of pieces1894 * @example repartitionToken(aliceKeyring, 10, 5, 12345n);1895 * @returns true if the repartion was success, otherwise false1896 */1897 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1898 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1899 const repartitionResult = await this.helper.executeExtrinsic(1900 signer,1901 'api.tx.unique.repartition', [collectionId, tokenId, amount],1902 true,1903 );1904 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1905 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1906 }1907}190819091910class FTGroup extends CollectionGroup {1911 /**1912 * Get collection object1913 * @param collectionId ID of collection1914 * @example getCollectionObject(2);1915 * @returns instance of UniqueFTCollection1916 */1917 getCollectionObject(collectionId: number): UniqueFTCollection {1918 return new UniqueFTCollection(collectionId, this.helper);1919 }19201921 /**1922 * Mint new fungible collection1923 * @param signer keyring of signer1924 * @param collectionOptions Collection options1925 * @param decimalPoints number of token decimals1926 * @example1927 * mintCollection(aliceKeyring, {1928 * name: 'New',1929 * description: 'New collection',1930 * tokenPrefix: 'NEW',1931 * }, 18)1932 * @returns newly created fungible collection1933 */1934 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1935 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1936 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1937 collectionOptions.mode = {fungible: decimalPoints};1938 for (const key of ['name', 'description', 'tokenPrefix']) {1939 if (typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1940 }1941 const creationResult = await this.helper.executeExtrinsic(1942 signer,1943 'api.tx.unique.createCollectionEx', [collectionOptions],1944 true,1945 );1946 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1947 }19481949 /**1950 * Mint tokens1951 * @param signer keyring of signer1952 * @param collectionId ID of collection1953 * @param owner address owner of new tokens1954 * @param amount amount of tokens to be meanted1955 * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);1956 * @returns ```true``` if extrinsic success, otherwise ```false```1957 */1958 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {1959 const creationResult = await this.helper.executeExtrinsic(1960 signer,1961 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {1962 fungible: {1963 value: amount,1964 },1965 }],1966 true, // `Unable to mint fungible tokens for ${label}`,1967 );1968 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1969 }19701971 /**1972 * Mint multiple Fungible tokens with one owner1973 * @param signer keyring of signer1974 * @param collectionId ID of collection1975 * @param owner tokens owner1976 * @param tokens array of tokens with properties and pieces1977 * @returns ```true``` if extrinsic success, otherwise ```false```1978 */1979 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {1980 const rawTokens = [];1981 for (const token of tokens) {1982 const raw = {Fungible: {Value: token.value}};1983 rawTokens.push(raw);1984 }1985 const creationResult = await this.helper.executeExtrinsic(1986 signer,1987 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1988 true,1989 );1990 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1991 }19921993 /**1994 * Get the top 10 owners with the largest balance for the Fungible collection1995 * @param collectionId ID of collection1996 * @example getTop10Owners(10);1997 * @returns array of ```ICrossAccountId```1998 */1999 async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2000 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2001 }20022003 /**2004 * Get account balance2005 * @param collectionId ID of collection2006 * @param addressObj address of owner2007 * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})2008 * @returns amount of fungible tokens owned by address2009 */2010 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2011 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2012 }20132014 /**2015 * Transfer tokens to address2016 * @param signer keyring of signer2017 * @param collectionId ID of collection2018 * @param toAddressObj address recipient2019 * @param amount amount of tokens to be sent2020 * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2021 * @returns ```true``` if extrinsic success, otherwise ```false```2022 */2023 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2024 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2025 }20262027 /**2028 * Transfer some tokens on behalf of the owner.2029 * @param signer keyring of signer2030 * @param collectionId ID of collection2031 * @param fromAddressObj address on behalf of which tokens will be sent2032 * @param toAddressObj address where token to be sent2033 * @param amount number of tokens to be sent2034 * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);2035 * @returns ```true``` if extrinsic success, otherwise ```false```2036 */2037 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2038 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2039 }20402041 /**2042 * Destroy some amount of tokens2043 * @param signer keyring of signer2044 * @param collectionId ID of collection2045 * @param amount amount of tokens to be destroyed2046 * @example burnTokens(aliceKeyring, 10, 1000n);2047 * @returns ```true``` if extrinsic success, otherwise ```false```2048 */2049 async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2050 return await super.burnToken(signer, collectionId, 0, amount);2051 }20522053 /**2054 * Burn some tokens on behalf of the owner.2055 * @param signer keyring of signer2056 * @param collectionId ID of collection2057 * @param fromAddressObj address on behalf of which tokens will be burnt2058 * @param amount amount of tokens to be burnt2059 * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);2060 * @returns ```true``` if extrinsic success, otherwise ```false```2061 */2062 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2063 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2064 }20652066 /**2067 * Get total collection supply2068 * @param collectionId2069 * @returns2070 */2071 async getTotalPieces(collectionId: number): Promise<bigint> {2072 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2073 }20742075 /**2076 * Set, change, or remove approved address to transfer tokens.2077 *2078 * @param signer keyring of signer2079 * @param collectionId ID of collection2080 * @param toAddressObj address to be approved2081 * @param amount amount of tokens to be approved2082 * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)2083 * @returns ```true``` if extrinsic success, otherwise ```false```2084 */2085 approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2086 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2087 }20882089 /**2090 * Get amount of fungible tokens approved to transfer2091 * @param collectionId ID of collection2092 * @param fromAddressObj owner of tokens2093 * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner2094 * @returns number of tokens approved for the transfer2095 */2096 getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2097 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2098 }2099}210021012102class ChainGroup extends HelperGroup<ChainHelperBase> {2103 /**2104 * Get system properties of a chain2105 * @example getChainProperties();2106 * @returns ss58Format, token decimals, and token symbol2107 */2108 getChainProperties(): IChainProperties {2109 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2110 return {2111 ss58Format: properties.ss58Format.toJSON(),2112 tokenDecimals: properties.tokenDecimals.toJSON(),2113 tokenSymbol: properties.tokenSymbol.toJSON(),2114 };2115 }21162117 /**2118 * Get chain header2119 * @example getLatestBlockNumber();2120 * @returns the number of the last block2121 */2122 async getLatestBlockNumber(): Promise<number> {2123 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2124 }21252126 /**2127 * Get block hash by block number2128 * @param blockNumber number of block2129 * @example getBlockHashByNumber(12345);2130 * @returns hash of a block2131 */2132 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2133 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2134 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2135 return blockHash;2136 }21372138 // TODO add docs2139 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2140 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2141 if (!blockHash) return null;2142 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2143 }21442145 /**2146 * Get account nonce2147 * @param address substrate address2148 * @example getNonce("5GrwvaEF5zXb26Fz...");2149 * @returns number, account's nonce2150 */2151 async getNonce(address: TSubstrateAccount): Promise<number> {2152 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2153 }2154}21552156class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2157 /**2158 * Get substrate address balance2159 * @param address substrate address2160 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2161 * @returns amount of tokens on address2162 */2163 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2164 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2165 }21662167 /**2168 * Transfer tokens to substrate address2169 * @param signer keyring of signer2170 * @param address substrate address of a recipient2171 * @param amount amount of tokens to be transfered2172 * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2173 * @returns ```true``` if extrinsic success, otherwise ```false```2174 */2175 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2176 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);21772178 let transfer = {from: null, to: null, amount: 0n} as any;2179 result.result.events.forEach(({event: {data, method, section}}) => {2180 if ((section === 'balances') && (method === 'Transfer')) {2181 transfer = {2182 from: this.helper.address.normalizeSubstrate(data[0]),2183 to: this.helper.address.normalizeSubstrate(data[1]),2184 amount: BigInt(data[2]),2185 };2186 }2187 });2188 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2189 && this.helper.address.normalizeSubstrate(address) === transfer.to2190 && BigInt(amount) === transfer.amount;2191 return isSuccess;2192 }21932194 /**2195 * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2196 * @param address substrate address2197 * @returns2198 */2199 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2200 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2201 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2202 }2203}22042205class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2206 /**2207 * Get ethereum address balance2208 * @param address ethereum address2209 * @example getEthereum("0x9F0583DbB855d...")2210 * @returns amount of tokens on address2211 */2212 async getEthereum(address: TEthereumAccount): Promise<bigint> {2213 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2214 }22152216 /**2217 * Transfer tokens to address2218 * @param signer keyring of signer2219 * @param address Ethereum address of a recipient2220 * @param amount amount of tokens to be transfered2221 * @example transferToEthereum(alithKeyring, "0x9F0583DbB855d...", 100_000_000_000n);2222 * @returns ```true``` if extrinsic success, otherwise ```false```2223 */2224 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2225 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22262227 let transfer = {from: null, to: null, amount: 0n} as any;2228 result.result.events.forEach(({event: {data, method, section}}) => {2229 if ((section === 'balances') && (method === 'Transfer')) {2230 transfer = {2231 from: data[0].toString(),2232 to: data[1].toString(),2233 amount: BigInt(data[2]),2234 };2235 }2236 });2237 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2238 && address === transfer.to2239 && BigInt(amount) === transfer.amount;2240 return isSuccess;2241 }2242}22432244class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2245 subBalanceGroup: SubstrateBalanceGroup<T>;2246 ethBalanceGroup: EthereumBalanceGroup<T>;22472248 constructor(helper: T) {2249 super(helper);2250 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2251 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2252 }22532254 getCollectionCreationPrice(): bigint {2255 return 2n * this.getOneTokenNominal();2256 }2257 /**2258 * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2259 * @example getOneTokenNominal()2260 * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.2261 */2262 getOneTokenNominal(): bigint {2263 const chainProperties = this.helper.chain.getChainProperties();2264 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2265 }22662267 /**2268 * Get substrate address balance2269 * @param address substrate address2270 * @example getSubstrate("5GrwvaEF5zXb26Fz...")2271 * @returns amount of tokens on address2272 */2273 getSubstrate(address: TSubstrateAccount): Promise<bigint> {2274 return this.subBalanceGroup.getSubstrate(address);2275 }22762277 /**2278 * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2279 * @param address substrate address2280 * @returns2281 */2282 getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2283 return this.subBalanceGroup.getSubstrateFull(address);2284 }22852286 /**2287 * Get ethereum address balance2288 * @param address ethereum address2289 * @example getEthereum("0x9F0583DbB855d...")2290 * @returns amount of tokens on address2291 */2292 getEthereum(address: TEthereumAccount): Promise<bigint> {2293 return this.ethBalanceGroup.getEthereum(address);2294 }22952296 /**2297 * Transfer tokens to substrate address2298 * @param signer keyring of signer2299 * @param address substrate address of a recipient2300 * @param amount amount of tokens to be transfered2301 * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2302 * @returns ```true``` if extrinsic success, otherwise ```false```2303 */2304 transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2305 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2306 }23072308 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2309 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);23102311 let transfer = {from: null, to: null, amount: 0n} as any;2312 result.result.events.forEach(({event: {data, method, section}}) => {2313 if ((section === 'balances') && (method === 'Transfer')) {2314 transfer = {2315 from: this.helper.address.normalizeSubstrate(data[0]),2316 to: this.helper.address.normalizeSubstrate(data[1]),2317 amount: BigInt(data[2]),2318 };2319 }2320 });2321 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2322 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2323 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2324 return isSuccess;2325 }2326}23272328class AddressGroup extends HelperGroup<ChainHelperBase> {2329 /**2330 * Normalizes the address to the specified ss58 format, by default ```42```.2331 * @param address substrate address2332 * @param ss58Format format for address conversion, by default ```42```2333 * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY2334 * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation2335 */2336 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2337 return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2338 }23392340 /**2341 * Get address in the connected chain format2342 * @param address substrate address2343 * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network2344 * @returns address in chain format2345 */2346 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2347 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2348 }23492350 /**2351 * Get substrate mirror of an ethereum address2352 * @param ethAddress ethereum address2353 * @param toChainFormat false for normalized account2354 * @example ethToSubstrate('0x9F0583DbB855d...')2355 * @returns substrate mirror of a provided ethereum address2356 */2357 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2358 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2359 }23602361 /**2362 * Get ethereum mirror of a substrate address2363 * @param subAddress substrate account2364 * @example substrateToEth("5DnSF6RRjwteE3BrC...")2365 * @returns ethereum mirror of a provided substrate address2366 */2367 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2368 return CrossAccountId.translateSubToEth(subAddress);2369 }23702371 paraSiblingSovereignAccount(paraid: number) {2372 // We are getting a *sibling* parachain sovereign account,2373 // so we need a sibling prefix: encoded(b"sibl") == 0x7369626c2374 const siblingPrefix = '0x7369626c';23752376 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2377 const suffix = '000000000000000000000000000000000000000000000000';23782379 return siblingPrefix + encodedParaId + suffix;2380 }2381}23822383class StakingGroup extends HelperGroup<UniqueHelper> {2384 /**2385 * Stake tokens for App Promotion2386 * @param signer keyring of signer2387 * @param amountToStake amount of tokens to stake2388 * @param label extra label for log2389 * @returns2390 */2391 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2392 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2393 const _stakeResult = await this.helper.executeExtrinsic(2394 signer, 'api.tx.appPromotion.stake',2395 [amountToStake], true,2396 );2397 // TODO extract info from stakeResult2398 return true;2399 }24002401 /**2402 * Unstake tokens for App Promotion2403 * @param signer keyring of signer2404 * @param amountToUnstake amount of tokens to unstake2405 * @param label extra label for log2406 * @returns block number where balances will be unlocked2407 */2408 async unstake(signer: TSigner, label?: string): Promise<number> {2409 if(typeof label === 'undefined') label = `${signer.address}`;2410 const _unstakeResult = await this.helper.executeExtrinsic(2411 signer, 'api.tx.appPromotion.unstake',2412 [], true,2413 );2414 // TODO extract block number fron events2415 return 1;2416 }24172418 /**2419 * Get total staked amount for address2420 * @param address substrate or ethereum address2421 * @returns total staked amount2422 */2423 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2424 if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2425 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2426 }24272428 /**2429 * Get total staked per block2430 * @param address substrate or ethereum address2431 * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block2432 */2433 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2434 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2435 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2436 return {2437 block: block.toBigInt(),2438 amount: amount.toBigInt(),2439 };2440 });2441 }24422443 /**2444 * Get total pending unstake amount for address2445 * @param address substrate or ethereum address2446 * @returns total pending unstake amount2447 */2448 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2449 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2450 }24512452 /**2453 * Get pending unstake amount per block for address2454 * @param address substrate or ethereum address2455 * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block2456 */2457 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2458 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2459 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2460 return {2461 block: block.toBigInt(),2462 amount: amount.toBigInt(),2463 };2464 });2465 return result;2466 }2467}24682469class SchedulerGroup extends HelperGroup<UniqueHelper> {2470 constructor(helper: UniqueHelper) {2471 super(helper);2472 }24732474 cancelScheduled(signer: TSigner, scheduledId: string) {2475 return this.helper.executeExtrinsic(2476 signer,2477 'api.tx.scheduler.cancelNamed',2478 [scheduledId],2479 true,2480 );2481 }24822483 changePriority(signer: TSigner, scheduledId: string, priority: number) {2484 return this.helper.executeExtrinsic(2485 signer,2486 'api.tx.scheduler.changeNamedPriority',2487 [scheduledId, priority],2488 true,2489 );2490 }24912492 scheduleAt<T extends UniqueHelper>(2493 scheduledId: string,2494 executionBlockNumber: number,2495 options: ISchedulerOptions = {},2496 ) {2497 return this.schedule<T>('scheduleNamed', scheduledId, executionBlockNumber, options);2498 }24992500 scheduleAfter<T extends UniqueHelper>(2501 scheduledId: string,2502 blocksBeforeExecution: number,2503 options: ISchedulerOptions = {},2504 ) {2505 return this.schedule<T>('scheduleNamedAfter', scheduledId, blocksBeforeExecution, options);2506 }25072508 schedule<T extends UniqueHelper>(2509 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter',2510 scheduledId: string,2511 blocksNum: number,2512 options: ISchedulerOptions = {},2513 ) {2514 // eslint-disable-next-line @typescript-eslint/naming-convention2515 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2516 return this.helper.clone(ScheduledHelperType, {2517 scheduleFn,2518 scheduledId,2519 blocksNum,2520 options,2521 }) as T;2522 }2523}25242525class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2526 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2527 await this.helper.executeExtrinsic(2528 signer,2529 'api.tx.foreignAssets.registerForeignAsset',2530 [ownerAddress, location, metadata],2531 true,2532 );2533 }25342535 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2536 await this.helper.executeExtrinsic(2537 signer,2538 'api.tx.foreignAssets.updateForeignAsset',2539 [foreignAssetId, location, metadata],2540 true,2541 );2542 }2543}25442545class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2546 palletName: string;25472548 constructor(helper: T, palletName: string) {2549 super(helper);25502551 this.palletName = palletName;2552 }25532554 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2555 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2556 }2557}25582559class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2560 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2561 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2562 }25632564 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2565 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2566 }25672568 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2569 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2570 }2571}25722573class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2574 async accounts(address: string, currencyId: any) {2575 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2576 return BigInt(free);2577 }2578}25792580class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2581 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2582 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2583 }25842585 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2586 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2587 }25882589 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2590 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2591 }25922593 async account(assetId: string | number, address: string) {2594 const accountAsset = (2595 await this.helper.callRpc('api.query.assets.account', [assetId, address])2596 ).toJSON()! as any;25972598 if (accountAsset !== null) {2599 return BigInt(accountAsset['balance']);2600 } else {2601 return null;2602 }2603 }2604}26052606class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2607 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2608 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2609 }2610}26112612class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2613 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2614 const apiPrefix = 'api.tx.assetManager.';26152616 const registerTx = this.helper.constructApiCall(2617 apiPrefix + 'registerForeignAsset',2618 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2619 );26202621 const setUnitsTx = this.helper.constructApiCall(2622 apiPrefix + 'setAssetUnitsPerSecond',2623 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2624 );26252626 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2627 const encodedProposal = batchCall?.method.toHex() || '';2628 return encodedProposal;2629 }26302631 async assetTypeId(location: any) {2632 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2633 }2634}26352636class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2637 async notePreimage(signer: TSigner, encodedProposal: string) {2638 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2639 }26402641 externalProposeMajority(proposalHash: string) {2642 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2643 }26442645 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2646 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2647 }26482649 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2650 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2651 }2652}26532654class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2655 collective: string;26562657 constructor(helper: MoonbeamHelper, collective: string) {2658 super(helper);26592660 this.collective = collective;2661 }26622663 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2664 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2665 }26662667 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2668 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2669 }26702671 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2672 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2673 }26742675 async proposalCount() {2676 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2677 }2678}26792680export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2681export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;26822683export class UniqueHelper extends ChainHelperBase {2684 balance: BalanceGroup<UniqueHelper>;2685 collection: CollectionGroup;2686 nft: NFTGroup;2687 rft: RFTGroup;2688 ft: FTGroup;2689 staking: StakingGroup;2690 scheduler: SchedulerGroup;2691 foreignAssets: ForeignAssetsGroup;2692 xcm: XcmGroup<UniqueHelper>;2693 xTokens: XTokensGroup<UniqueHelper>;2694 tokens: TokensGroup<UniqueHelper>;26952696 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2697 super(logger, options.helperBase ?? UniqueHelper);26982699 this.balance = new BalanceGroup(this);2700 this.collection = new CollectionGroup(this);2701 this.nft = new NFTGroup(this);2702 this.rft = new RFTGroup(this);2703 this.ft = new FTGroup(this);2704 this.staking = new StakingGroup(this);2705 this.scheduler = new SchedulerGroup(this);2706 this.foreignAssets = new ForeignAssetsGroup(this);2707 this.xcm = new XcmGroup(this, 'polkadotXcm');2708 this.xTokens = new XTokensGroup(this);2709 this.tokens = new TokensGroup(this);2710 }27112712 getSudo<T extends UniqueHelper>() {2713 // eslint-disable-next-line @typescript-eslint/naming-convention2714 const SudoHelperType = SudoHelper(this.helperBase);2715 return this.clone(SudoHelperType) as T;2716 }2717}27182719export class XcmChainHelper extends ChainHelperBase {2720 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2721 const wsProvider = new WsProvider(wsEndpoint);2722 this.api = new ApiPromise({2723 provider: wsProvider,2724 });2725 await this.api.isReadyOrError;2726 this.network = await UniqueHelper.detectNetwork(this.api);2727 }2728}27292730export class RelayHelper extends XcmChainHelper {2731 xcm: XcmGroup<RelayHelper>;27322733 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2734 super(logger, options.helperBase ?? RelayHelper);27352736 this.xcm = new XcmGroup(this, 'xcmPallet');2737 }2738}27392740export class WestmintHelper extends XcmChainHelper {2741 balance: SubstrateBalanceGroup<WestmintHelper>;2742 xcm: XcmGroup<WestmintHelper>;2743 assets: AssetsGroup<WestmintHelper>;2744 xTokens: XTokensGroup<WestmintHelper>;27452746 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2747 super(logger, options.helperBase ?? WestmintHelper);27482749 this.balance = new SubstrateBalanceGroup(this);2750 this.xcm = new XcmGroup(this, 'polkadotXcm');2751 this.assets = new AssetsGroup(this);2752 this.xTokens = new XTokensGroup(this);2753 }2754}27552756export class MoonbeamHelper extends XcmChainHelper {2757 balance: EthereumBalanceGroup<MoonbeamHelper>;2758 assetManager: MoonbeamAssetManagerGroup;2759 assets: AssetsGroup<MoonbeamHelper>;2760 xTokens: XTokensGroup<MoonbeamHelper>;2761 democracy: MoonbeamDemocracyGroup;2762 collective: {2763 council: MoonbeamCollectiveGroup,2764 techCommittee: MoonbeamCollectiveGroup,2765 };27662767 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2768 super(logger, options.helperBase ?? MoonbeamHelper);27692770 this.balance = new EthereumBalanceGroup(this);2771 this.assetManager = new MoonbeamAssetManagerGroup(this);2772 this.assets = new AssetsGroup(this);2773 this.xTokens = new XTokensGroup(this);2774 this.democracy = new MoonbeamDemocracyGroup(this);2775 this.collective = {2776 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2777 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2778 };2779 }2780}27812782export class AcalaHelper extends XcmChainHelper {2783 balance: SubstrateBalanceGroup<AcalaHelper>;2784 assetRegistry: AcalaAssetRegistryGroup;2785 xTokens: XTokensGroup<AcalaHelper>;2786 tokens: TokensGroup<AcalaHelper>;27872788 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2789 super(logger, options.helperBase ?? AcalaHelper);27902791 this.balance = new SubstrateBalanceGroup(this);2792 this.assetRegistry = new AcalaAssetRegistryGroup(this);2793 this.xTokens = new XTokensGroup(this);2794 this.tokens = new TokensGroup(this);2795 }27962797 getSudo<T extends AcalaHelper>() {2798 // eslint-disable-next-line @typescript-eslint/naming-convention2799 const SudoHelperType = SudoHelper(this.helperBase);2800 return this.clone(SudoHelperType) as T;2801 }2802}28032804// eslint-disable-next-line @typescript-eslint/naming-convention2805function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2806 return class extends Base {2807 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter';2808 scheduledId: string;2809 blocksNum: number;2810 options: ISchedulerOptions;28112812 constructor(...args: any[]) {2813 const logger = args[0] as ILogger;2814 const options = args[1] as {2815 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter',2816 scheduledId: string,2817 blocksNum: number,2818 options: ISchedulerOptions2819 };28202821 super(logger);28222823 this.scheduleFn = options.scheduleFn;2824 this.scheduledId = options.scheduledId;2825 this.blocksNum = options.blocksNum;2826 this.options = options.options;2827 }28282829 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {2830 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);2831 const extrinsic = 'api.tx.scheduler.' + this.scheduleFn;28322833 return super.executeExtrinsic(2834 sender,2835 extrinsic,2836 [2837 this.scheduledId,2838 this.blocksNum,2839 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,2840 this.options.priority ?? null,2841 scheduledTx,2842 ],2843 expectSuccess,2844 );2845 }2846 };2847}28482849// eslint-disable-next-line @typescript-eslint/naming-convention2850function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {2851 return class extends Base {2852 constructor(...args: any[]) {2853 super(...args);2854 }28552856 executeExtrinsic (2857 sender: IKeyringPair,2858 extrinsic: string,2859 params: any[],2860 expectSuccess?: boolean,2861 ): Promise<ITransactionResult> {2862 const call = this.constructApiCall(extrinsic, params);2863 return super.executeExtrinsic(2864 sender,2865 'api.tx.sudo.sudo',2866 [call],2867 expectSuccess,2868 );2869 }2870 };2871}28722873export class UniqueBaseCollection {2874 helper: UniqueHelper;2875 collectionId: number;28762877 constructor(collectionId: number, uniqueHelper: UniqueHelper) {2878 this.collectionId = collectionId;2879 this.helper = uniqueHelper;2880 }28812882 async getData() {2883 return await this.helper.collection.getData(this.collectionId);2884 }28852886 async getLastTokenId() {2887 return await this.helper.collection.getLastTokenId(this.collectionId);2888 }28892890 async doesTokenExist(tokenId: number) {2891 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);2892 }28932894 async getAdmins() {2895 return await this.helper.collection.getAdmins(this.collectionId);2896 }28972898 async getAllowList() {2899 return await this.helper.collection.getAllowList(this.collectionId);2900 }29012902 async getEffectiveLimits() {2903 return await this.helper.collection.getEffectiveLimits(this.collectionId);2904 }29052906 async getProperties(propertyKeys?: string[] | null) {2907 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);2908 }29092910 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {2911 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);2912 }29132914 async getOptions() {2915 return await this.helper.collection.getCollectionOptions(this.collectionId);2916 }29172918 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {2919 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);2920 }29212922 async confirmSponsorship(signer: TSigner) {2923 return await this.helper.collection.confirmSponsorship(signer, this.collectionId);2924 }29252926 async removeSponsor(signer: TSigner) {2927 return await this.helper.collection.removeSponsor(signer, this.collectionId);2928 }29292930 async setLimits(signer: TSigner, limits: ICollectionLimits) {2931 return await this.helper.collection.setLimits(signer, this.collectionId, limits);2932 }29332934 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {2935 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);2936 }29372938 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {2939 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);2940 }29412942 async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {2943 return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);2944 }29452946 async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {2947 return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);2948 }29492950 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {2951 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);2952 }29532954 async setProperties(signer: TSigner, properties: IProperty[]) {2955 return await this.helper.collection.setProperties(signer, this.collectionId, properties);2956 }29572958 async deleteProperties(signer: TSigner, propertyKeys: string[]) {2959 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);2960 }29612962 async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {2963 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);2964 }29652966 async enableNesting(signer: TSigner, permissions: INestingPermissions) {2967 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);2968 }29692970 async disableNesting(signer: TSigner) {2971 return await this.helper.collection.disableNesting(signer, this.collectionId);2972 }29732974 async burn(signer: TSigner) {2975 return await this.helper.collection.burn(signer, this.collectionId);2976 }29772978 scheduleAt<T extends UniqueHelper>(2979 scheduledId: string,2980 executionBlockNumber: number,2981 options: ISchedulerOptions = {},2982 ) {2983 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);2984 return new UniqueBaseCollection(this.collectionId, scheduledHelper);2985 }29862987 scheduleAfter<T extends UniqueHelper>(2988 scheduledId: string,2989 blocksBeforeExecution: number,2990 options: ISchedulerOptions = {},2991 ) {2992 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);2993 return new UniqueBaseCollection(this.collectionId, scheduledHelper);2994 }29952996 getSudo<T extends UniqueHelper>() {2997 return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());2998 }2999}300030013002export class UniqueNFTCollection extends UniqueBaseCollection {3003 getTokenObject(tokenId: number) {3004 return new UniqueNFToken(tokenId, this);3005 }30063007 async getTokensByAddress(addressObj: ICrossAccountId) {3008 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3009 }30103011 async getToken(tokenId: number, blockHashAt?: string) {3012 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3013 }30143015 async getTokenOwner(tokenId: number, blockHashAt?: string) {3016 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3017 }30183019 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3020 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3021 }30223023 async getTokenChildren(tokenId: number, blockHashAt?: string) {3024 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3025 }30263027 async getPropertyPermissions(propertyKeys: string[] | null = null) {3028 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3029 }30303031 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3032 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3033 }30343035 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3036 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3037 }30383039 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3040 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3041 }30423043 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3044 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3045 }30463047 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3048 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3049 }30503051 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3052 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3053 }30543055 async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3056 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3057 }30583059 async burnToken(signer: TSigner, tokenId: number) {3060 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3061 }30623063 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3064 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3065 }30663067 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3068 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3069 }30703071 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3072 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3073 }30743075 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3076 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3077 }30783079 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3080 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3081 }30823083 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3084 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3085 }30863087 scheduleAt<T extends UniqueHelper>(3088 scheduledId: string,3089 executionBlockNumber: number,3090 options: ISchedulerOptions = {},3091 ) {3092 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3093 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3094 }30953096 scheduleAfter<T extends UniqueHelper>(3097 scheduledId: string,3098 blocksBeforeExecution: number,3099 options: ISchedulerOptions = {},3100 ) {3101 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3102 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3103 }31043105 getSudo<T extends UniqueHelper>() {3106 return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3107 }3108}310931103111export class UniqueRFTCollection extends UniqueBaseCollection {3112 getTokenObject(tokenId: number) {3113 return new UniqueRFToken(tokenId, this);3114 }31153116 async getToken(tokenId: number, blockHashAt?: string) {3117 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3118 }31193120 async getTokensByAddress(addressObj: ICrossAccountId) {3121 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3122 }31233124 async getTop10TokenOwners(tokenId: number) {3125 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3126 }31273128 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3129 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3130 }31313132 async getTokenTotalPieces(tokenId: number) {3133 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3134 }31353136 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3137 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3138 }31393140 async getPropertyPermissions(propertyKeys: string[] | null = null) {3141 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3142 }31433144 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3145 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3146 }31473148 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3149 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3150 }31513152 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3153 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3154 }31553156 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3157 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3158 }31593160 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3161 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3162 }31633164 async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3165 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3166 }31673168 async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3169 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3170 }31713172 async burnToken(signer: TSigner, tokenId: number, amount=1n) {3173 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3174 }31753176 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n) {3177 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3178 }31793180 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3181 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3182 }31833184 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3185 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3186 }31873188 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3189 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3190 }31913192 scheduleAt<T extends UniqueHelper>(3193 scheduledId: string,3194 executionBlockNumber: number,3195 options: ISchedulerOptions = {},3196 ) {3197 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3198 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3199 }32003201 scheduleAfter<T extends UniqueHelper>(3202 scheduledId: string,3203 blocksBeforeExecution: number,3204 options: ISchedulerOptions = {},3205 ) {3206 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3207 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3208 }32093210 getSudo<T extends UniqueHelper>() {3211 return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3212 }3213}321432153216export class UniqueFTCollection extends UniqueBaseCollection {3217 async getBalance(addressObj: ICrossAccountId) {3218 return await this.helper.ft.getBalance(this.collectionId, addressObj);3219 }32203221 async getTotalPieces() {3222 return await this.helper.ft.getTotalPieces(this.collectionId);3223 }32243225 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3226 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3227 }32283229 async getTop10Owners() {3230 return await this.helper.ft.getTop10Owners(this.collectionId);3231 }32323233 async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3234 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3235 }32363237 async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3238 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3239 }32403241 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3242 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3243 }32443245 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3246 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3247 }32483249 async burnTokens(signer: TSigner, amount=1n) {3250 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3251 }32523253 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3254 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3255 }32563257 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3258 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3259 }32603261 scheduleAt<T extends UniqueHelper>(3262 scheduledId: string,3263 executionBlockNumber: number,3264 options: ISchedulerOptions = {},3265 ) {3266 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3267 return new UniqueFTCollection(this.collectionId, scheduledHelper);3268 }32693270 scheduleAfter<T extends UniqueHelper>(3271 scheduledId: string,3272 blocksBeforeExecution: number,3273 options: ISchedulerOptions = {},3274 ) {3275 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3276 return new UniqueFTCollection(this.collectionId, scheduledHelper);3277 }32783279 getSudo<T extends UniqueHelper>() {3280 return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3281 }3282}328332843285export class UniqueBaseToken {3286 collection: UniqueNFTCollection | UniqueRFTCollection;3287 collectionId: number;3288 tokenId: number;32893290 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3291 this.collection = collection;3292 this.collectionId = collection.collectionId;3293 this.tokenId = tokenId;3294 }32953296 async getNextSponsored(addressObj: ICrossAccountId) {3297 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3298 }32993300 async getProperties(propertyKeys?: string[] | null) {3301 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3302 }33033304 async setProperties(signer: TSigner, properties: IProperty[]) {3305 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3306 }33073308 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3309 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3310 }33113312 async doesExist() {3313 return await this.collection.doesTokenExist(this.tokenId);3314 }33153316 nestingAccount() {3317 return this.collection.helper.util.getTokenAccount(this);3318 }33193320 scheduleAt<T extends UniqueHelper>(3321 scheduledId: string,3322 executionBlockNumber: number,3323 options: ISchedulerOptions = {},3324 ) {3325 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3326 return new UniqueBaseToken(this.tokenId, scheduledCollection);3327 }33283329 scheduleAfter<T extends UniqueHelper>(3330 scheduledId: string,3331 blocksBeforeExecution: number,3332 options: ISchedulerOptions = {},3333 ) {3334 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3335 return new UniqueBaseToken(this.tokenId, scheduledCollection);3336 }33373338 getSudo<T extends UniqueHelper>() {3339 return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3340 }3341}334233433344export class UniqueNFToken extends UniqueBaseToken {3345 collection: UniqueNFTCollection;33463347 constructor(tokenId: number, collection: UniqueNFTCollection) {3348 super(tokenId, collection);3349 this.collection = collection;3350 }33513352 async getData(blockHashAt?: string) {3353 return await this.collection.getToken(this.tokenId, blockHashAt);3354 }33553356 async getOwner(blockHashAt?: string) {3357 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3358 }33593360 async getTopmostOwner(blockHashAt?: string) {3361 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3362 }33633364 async getChildren(blockHashAt?: string) {3365 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3366 }33673368 async nest(signer: TSigner, toTokenObj: IToken) {3369 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3370 }33713372 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3373 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3374 }33753376 async transfer(signer: TSigner, addressObj: ICrossAccountId) {3377 return await this.collection.transferToken(signer, this.tokenId, addressObj);3378 }33793380 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3381 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3382 }33833384 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3385 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3386 }33873388 async isApproved(toAddressObj: ICrossAccountId) {3389 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3390 }33913392 async burn(signer: TSigner) {3393 return await this.collection.burnToken(signer, this.tokenId);3394 }33953396 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3397 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3398 }33993400 scheduleAt<T extends UniqueHelper>(3401 scheduledId: string,3402 executionBlockNumber: number,3403 options: ISchedulerOptions = {},3404 ) {3405 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3406 return new UniqueNFToken(this.tokenId, scheduledCollection);3407 }34083409 scheduleAfter<T extends UniqueHelper>(3410 scheduledId: string,3411 blocksBeforeExecution: number,3412 options: ISchedulerOptions = {},3413 ) {3414 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3415 return new UniqueNFToken(this.tokenId, scheduledCollection);3416 }34173418 getSudo<T extends UniqueHelper>() {3419 return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3420 }3421}34223423export class UniqueRFToken extends UniqueBaseToken {3424 collection: UniqueRFTCollection;34253426 constructor(tokenId: number, collection: UniqueRFTCollection) {3427 super(tokenId, collection);3428 this.collection = collection;3429 }34303431 async getData(blockHashAt?: string) {3432 return await this.collection.getToken(this.tokenId, blockHashAt);3433 }34343435 async getTop10Owners() {3436 return await this.collection.getTop10TokenOwners(this.tokenId);3437 }34383439 async getBalance(addressObj: ICrossAccountId) {3440 return await this.collection.getTokenBalance(this.tokenId, addressObj);3441 }34423443 async getTotalPieces() {3444 return await this.collection.getTokenTotalPieces(this.tokenId);3445 }34463447 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3448 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3449 }34503451 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3452 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3453 }34543455 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3456 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3457 }34583459 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3460 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3461 }34623463 async repartition(signer: TSigner, amount: bigint) {3464 return await this.collection.repartitionToken(signer, this.tokenId, amount);3465 }34663467 async burn(signer: TSigner, amount=1n) {3468 return await this.collection.burnToken(signer, this.tokenId, amount);3469 }34703471 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3472 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3473 }34743475 scheduleAt<T extends UniqueHelper>(3476 scheduledId: string,3477 executionBlockNumber: number,3478 options: ISchedulerOptions = {},3479 ) {3480 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3481 return new UniqueRFToken(this.tokenId, scheduledCollection);3482 }34833484 scheduleAfter<T extends UniqueHelper>(3485 scheduledId: string,3486 blocksBeforeExecution: number,3487 options: ISchedulerOptions = {},3488 ) {3489 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3490 return new UniqueRFToken(this.tokenId, scheduledCollection);3491 }34923493 getSudo<T extends UniqueHelper>() {3494 return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3495 }3496}