git.delta.rocks / unique-network / refs/commits / 0ce92f01d899

difftreelog

fix tests cleanup

Andy Smith2023-08-29parent: #905f9b3.patch.diff
in: master

10 files changed

modifiedtests/src/eth/collectionLimits.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collectionLimits.test.ts
+++ b/tests/src/eth/collectionLimits.test.ts
@@ -97,7 +97,7 @@
 
       const owner = await helper.eth.createAccountWithBalance(donor);
       const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'ISNI', testCase.case, 18)).send();
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
 
       // Cannot set non-existing limit
       await expect(collectionEvm.methods
modifiedtests/src/eth/createCollection.test.tsdiffbeforeafterboth
--- a/tests/src/eth/createCollection.test.ts
+++ b/tests/src/eth/createCollection.test.ts
@@ -23,12 +23,13 @@
 
 const DECIMALS = 18;
 const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [
-  [],
-  [],
-  [],
-  [false, false, []],
-  [],
-  [0],
+  [],  // properties
+  [],  // tokenPropertyPermissions
+  [],  // adminList
+  [false, false, []],  // nestingSettings
+  [],  // limits
+  emptyAddress,  // pendingSponsor
+  [0],  // flags
 ];
 
 type ElementOf<A> = A extends readonly (infer T)[] ? T : never;
@@ -64,7 +65,7 @@
     itEth('Collection address exist', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
       const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
 
       expect(await collectionHelpers
         .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
@@ -76,7 +77,7 @@
         .to.be.true;
 
       // check collectionOwner:
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
       const collectionOwner = await collectionEvm.methods.collectionOwner().call();
       expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
     });
@@ -84,7 +85,7 @@
     itEth('destroyCollection', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
       const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();
-      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
 
       const result = await collectionHelper.methods
         .destroyCollection(collectionAddress)
@@ -110,7 +111,7 @@
 
     itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
       {
         const MAX_NAME_LENGTH = 64;
         const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
@@ -119,7 +120,6 @@
 
         await expect(collectionHelper.methods
           .createCollection([
-            emptyAddress,
             collectionName,
             description,
             tokenPrefix,
@@ -136,7 +136,6 @@
         const tokenPrefix = 'A';
         await expect(collectionHelper.methods
           .createCollection([
-            emptyAddress,
             collectionName,
             description,
             tokenPrefix,
@@ -153,7 +152,6 @@
         const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
         await expect(collectionHelper.methods
           .createCollection([
-            emptyAddress,
             collectionName,
             description,
             tokenPrefix,
@@ -167,11 +165,10 @@
 
     itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
       const expects = [0n, 1n, 30n].map(async value => {
         await expect(collectionHelper.methods
           .createCollection([
-            emptyAddress,
             'Peasantry',
             'absolutely anything',
             'TWIW',
@@ -238,7 +235,7 @@
 
       const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
       const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
-      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
 
       expect(await collectionHelpers.methods
         .isCollectionExist(expectedCollectionAddress)
@@ -246,7 +243,6 @@
 
       await collectionHelpers.methods
         .createCollection([
-          emptyAddress,
           'A',
           'A',
           'A',
@@ -331,7 +327,7 @@
       const ss58Format = helper.chain.getChainProperties().ss58Format;
       const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Sponsor', 'absolutely anything', 'ENVY', 'rft')).send();
 
-      const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+      const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
       const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
       await collection.methods.setCollectionSponsorCross(sponsorCross).send();
 
@@ -340,7 +336,7 @@
 
       await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
 
-      const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
       await sponsorCollection.methods.confirmCollectionSponsorship().send();
 
       data = (await helper.rft.getData(collectionId))!;
@@ -350,7 +346,7 @@
     itEth('Collection address exist', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
       const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
 
       expect(await collectionHelpers
         .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
@@ -362,7 +358,7 @@
         .to.be.true;
 
       // check collectionOwner:
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
       const collectionOwner = await collectionEvm.methods.collectionOwner().call();
       expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
     });
@@ -370,7 +366,7 @@
     itEth('destroyCollection', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
       const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'OLF', 'rft')).send();
-      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
 
       await expect(collectionHelper.methods
         .destroyCollection(collectionAddress)
@@ -384,7 +380,7 @@
 
     itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
       {
         const MAX_NAME_LENGTH = 64;
         const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
@@ -393,7 +389,6 @@
 
         await expect(collectionHelper.methods
           .createCollection([
-            emptyAddress,
             collectionName,
             description,
             tokenPrefix,
@@ -410,7 +405,6 @@
         const tokenPrefix = 'A';
         await expect(collectionHelper.methods
           .createCollection([
-            emptyAddress,
             collectionName,
             description,
             tokenPrefix,
@@ -427,7 +421,6 @@
         const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
         await expect(collectionHelper.methods
           .createCollection([
-            emptyAddress,
             collectionName,
             description,
             tokenPrefix,
@@ -441,10 +434,9 @@
 
     itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
-      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+      const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
       await expect(collectionHelper.methods
         .createCollection([
-          emptyAddress,
           'Peasantry',
           'absolutely anything',
           'TWIW',
@@ -473,7 +465,7 @@
           pendingSponsor: sponsorCross,
         },
       ).send();
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
 
       expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;
 
@@ -657,7 +649,7 @@
         '',
       );
       const collectionSub = helper.nft.getCollectionObject(collectionId);
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
 
       // Set and confirm sponsor:
       await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});
@@ -695,7 +687,7 @@
           '',
         );
         const receiver = await helper.eth.createAccountWithBalance(donor);
-        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
 
         await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
 
@@ -764,7 +756,7 @@
             adminList: [adminCrossSub, adminCrossEth],
           },
         ).send();
-        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);
+        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);
 
         // 1. Expect api.rpc.unique.adminlist returns admins:
         const adminListRpc = await helper.collection.getAdmins(collectionId);
@@ -801,7 +793,7 @@
         },
         'uri',
       );
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
 
       // admin (sub and eth) can mint token:
       await collectionEvm.methods.mint(owner).send({from: adminEth});
@@ -852,7 +844,7 @@
         },
       ).send();
 
-      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
 
       {
         const adminList = await helper.collection.getAdmins(collectionId);
@@ -1062,7 +1054,7 @@
           },
         ).send();
 
-        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
+        const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
 
         await collectionEvm.methods.deleteCollectionProperties(['testKey1', 'testKey2']).send({from: caller});
 
@@ -1162,7 +1154,7 @@
               ],
             },
           ).send();
-          const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+          const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
 
           expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
             key: 'testKey',
@@ -1287,7 +1279,7 @@
           },
         ).send();
 
-        const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
+        const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);
         const tokenId = (await collection.methods.mintCross(receiver, [{key: 'testKey', value: Buffer.from('testValue')}, {key: 'testKey_1', value: Buffer.from('testValue_1')}]).send()).events.Transfer.returnValues.tokenId;
         expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(2);
 
@@ -1311,7 +1303,7 @@
         },
       ).send();
 
-      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
 
       // Create a token to be nested to
       const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});
@@ -1342,7 +1334,7 @@
         new CreateCollectionData('A', 'B', 'C', 'nft'),
       ).send();
 
-      const unnestedContract = await helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);
+      const unnestedContract = helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);
       expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
 
       const {collectionAddress} = await helper.eth.createCollection(
@@ -1357,7 +1349,7 @@
         },
       ).send();
 
-      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
       expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress.toString()]]);
       await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});
       expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);
@@ -1378,7 +1370,7 @@
         },
       ).send();
 
-      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
 
       // Create a token to nest into
       const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});
@@ -1419,17 +1411,15 @@
       }
     });
 
-    itEth('NFT: foreign flag number is ignored', async ({helper}) => {
+    itEth('NFT: can\'t set foreign flag number', async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
 
       {
-        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).send();
-        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});
+        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
       }
 
       {
-        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).send();
-        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});
+        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
       }
     });
 
@@ -1446,13 +1436,11 @@
       const owner = await helper.eth.createAccountWithBalance(donor);
 
       {
-        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).send();
-        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});
+        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
       }
 
       {
-        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).send();
-        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});
+        await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);
       }
     });
   });
modifiedtests/src/eth/createFTCollection.seqtest.tsdiffbeforeafterboth
--- a/tests/src/eth/createFTCollection.seqtest.ts
+++ b/tests/src/eth/createFTCollection.seqtest.ts
@@ -59,7 +59,7 @@
 
     const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
     const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
-    const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
 
     expect(await collectionHelpers.methods
       .isCollectionExist(expectedCollectionAddress)
modifiedtests/src/eth/createFTCollection.test.tsdiffbeforeafterboth
--- a/tests/src/eth/createFTCollection.test.ts
+++ b/tests/src/eth/createFTCollection.test.ts
@@ -50,7 +50,7 @@
 
     await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
 
-    const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
     await sponsorCollection.methods.confirmCollectionSponsorship().send();
 
     data = (await helper.rft.getData(collectionId))!;
@@ -64,7 +64,7 @@
     const description = 'absolutely anything';
     const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY');
 
-    const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
     const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
     await collection.methods.setCollectionSponsorCross(sponsorCross).send();
 
@@ -73,7 +73,7 @@
 
     await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
 
-    const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
     await sponsorCollection.methods.confirmCollectionSponsorship().send();
 
     data = (await helper.rft.getData(collectionId))!;
@@ -84,7 +84,7 @@
   itEth('Collection address exist', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-    const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
 
     expect(await collectionHelpers
       .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
@@ -96,7 +96,7 @@
       .to.be.true;
 
     // check collectionOwner:
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
     const collectionOwner = await collectionEvm.methods.collectionOwner().call();
     expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
   });
@@ -104,7 +104,7 @@
   itEth('destroyCollection', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const {collectionAddress, collectionId} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT');
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
 
     const result = await collectionHelper.methods
       .destroyCollection(collectionAddress)
@@ -143,7 +143,7 @@
 
   itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
     {
       const MAX_NAME_LENGTH = 64;
       const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
@@ -176,7 +176,7 @@
 
   itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
     const expects = [0n, 1n, 30n].map(async value => {
       await expect(collectionHelper.methods
         .createFTCollection('Peasantry', DECIMALS, 'absolutely anything', 'TWIW')
@@ -190,7 +190,7 @@
     const owner = await helper.eth.createAccountWithBalance(donor);
     const peasant = helper.eth.createAccount();
     const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE');
-    const peasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'ft', peasant, true);
+    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant, true);
     const EXPECTED_ERROR = 'NoPermission';
     {
       const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -198,7 +198,7 @@
         .setCollectionSponsor(sponsor)
         .call()).to.be.rejectedWith(EXPECTED_ERROR);
 
-      const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor, true);
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor, true);
       await expect(sponsorCollection.methods
         .confirmCollectionSponsorship()
         .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -214,7 +214,7 @@
     const owner = await helper.eth.createAccountWithBalance(donor);
     const peasant = helper.eth.createAccount();
     const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE');
-    const peasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'ft', peasant);
+    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant);
     const EXPECTED_ERROR = 'NoPermission';
     {
       const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -223,7 +223,7 @@
         .setCollectionSponsorCross(sponsorCross)
         .call()).to.be.rejectedWith(EXPECTED_ERROR);
 
-      const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor);
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor);
       await expect(sponsorCollection.methods
         .confirmCollectionSponsorship()
         .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
modifiedtests/src/eth/createNFTCollection.seqtest.tsdiffbeforeafterboth
--- a/tests/src/eth/createNFTCollection.seqtest.ts
+++ b/tests/src/eth/createNFTCollection.seqtest.ts
@@ -72,7 +72,7 @@
 
     const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
     const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
-    const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
 
     expect(await collectionHelpers.methods
       .isCollectionExist(expectedCollectionAddress)
modifiedtests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth
--- a/tests/src/eth/createNFTCollection.test.ts
+++ b/tests/src/eth/createNFTCollection.test.ts
@@ -39,7 +39,7 @@
     const baseUri = 'BaseURI';
 
     const {collectionId, collectionAddress, events} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri);
-    const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
 
     expect(events).to.be.deep.equal([
       {
@@ -82,7 +82,7 @@
     const ss58Format = helper.chain.getChainProperties().ss58Format;
     const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
 
-    const collection = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
     await collection.methods.setCollectionSponsor(sponsor).send();
 
     let data = (await helper.nft.getData(collectionId))!;
@@ -90,7 +90,7 @@
 
     await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
 
-    const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
     await sponsorCollection.methods.confirmCollectionSponsorship().send();
 
     data = (await helper.nft.getData(collectionId))!;
@@ -104,7 +104,7 @@
     const description = 'absolutely anything';
     const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', description, 'ROC');
 
-    const collection = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
     const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
     await collection.methods.setCollectionSponsorCross(sponsorCross).send();
 
@@ -125,7 +125,7 @@
   itEth('Collection address exist', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-    const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
 
     expect(await collectionHelpers
       .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
@@ -137,7 +137,7 @@
       .to.be.true;
 
     // check collectionOwner:
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
     const collectionOwner = await collectionEvm.methods.collectionOwner().call();
     expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
   });
@@ -156,7 +156,7 @@
 
   itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
     {
       const MAX_NAME_LENGTH = 64;
       const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
@@ -190,7 +190,7 @@
 
   itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
     await expect(collectionHelper.methods
       .createNFTCollection('Peasantry', 'absolutely anything', 'CVE')
       .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
@@ -209,7 +209,7 @@
         .setCollectionSponsor(sponsor)
         .call()).to.be.rejectedWith(EXPECTED_ERROR);
 
-      const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
       await expect(sponsorCollection.methods
         .confirmCollectionSponsorship()
         .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -225,7 +225,7 @@
     const owner = await helper.eth.createAccountWithBalance(donor);
     const malfeasant = helper.eth.createAccount();
     const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
-    const malfeasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);
+    const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);
     const EXPECTED_ERROR = 'NoPermission';
     {
       const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -234,7 +234,7 @@
         .setCollectionSponsorCross(sponsorCross)
         .call()).to.be.rejectedWith(EXPECTED_ERROR);
 
-      const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
       await expect(sponsorCollection.methods
         .confirmCollectionSponsorship()
         .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -249,7 +249,7 @@
   itEth('destroyCollection', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
 
 
     const result = await collectionHelper.methods
modifiedtests/src/eth/createRFTCollection.test.tsdiffbeforeafterboth
--- a/tests/src/eth/createRFTCollection.test.ts
+++ b/tests/src/eth/createRFTCollection.test.ts
@@ -95,7 +95,7 @@
     const ss58Format = helper.chain.getChainProperties().ss58Format;
     const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
 
-    const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true);
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true);
     await collection.methods.setCollectionSponsor(sponsor).send();
 
     let data = (await helper.rft.getData(collectionId))!;
@@ -103,7 +103,7 @@
 
     await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
 
-    const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
     await sponsorCollection.methods.confirmCollectionSponsorship().send();
 
     data = (await helper.rft.getData(collectionId))!;
@@ -116,7 +116,7 @@
     const ss58Format = helper.chain.getChainProperties().ss58Format;
     const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
 
-    const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
     const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
     await collection.methods.setCollectionSponsorCross(sponsorCross).send();
 
@@ -125,7 +125,7 @@
 
     await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
 
-    const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
     await sponsorCollection.methods.confirmCollectionSponsorship().send();
 
     data = (await helper.rft.getData(collectionId))!;
@@ -135,7 +135,7 @@
   itEth('Collection address exist', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-    const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
 
     expect(await collectionHelpers
       .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
@@ -147,7 +147,7 @@
       .to.be.true;
 
     // check collectionOwner:
-    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
     const collectionOwner = await collectionEvm.methods.collectionOwner().call();
     expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
   });
@@ -167,7 +167,7 @@
 
   itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
     {
       const MAX_NAME_LENGTH = 64;
       const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
@@ -200,7 +200,7 @@
 
   itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
     await expect(collectionHelper.methods
       .createRFTCollection('Peasantry', 'absolutely anything', 'TWIW')
       .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
@@ -211,7 +211,7 @@
     const owner = await helper.eth.createAccountWithBalance(donor);
     const peasant = helper.eth.createAccount();
     const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
-    const peasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', peasant, true);
+    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant, true);
     const EXPECTED_ERROR = 'NoPermission';
     {
       const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -219,7 +219,7 @@
         .setCollectionSponsor(sponsor)
         .call()).to.be.rejectedWith(EXPECTED_ERROR);
 
-      const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
       await expect(sponsorCollection.methods
         .confirmCollectionSponsorship()
         .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -235,7 +235,7 @@
     const owner = await helper.eth.createAccountWithBalance(donor);
     const peasant = helper.eth.createAccount();
     const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
-    const peasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);
+    const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);
     const EXPECTED_ERROR = 'NoPermission';
     {
       const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -244,7 +244,7 @@
         .setCollectionSponsorCross(sponsorCross)
         .call()).to.be.rejectedWith(EXPECTED_ERROR);
 
-      const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
       await expect(sponsorCollection.methods
         .confirmCollectionSponsorship()
         .call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -259,7 +259,7 @@
   itEth('destroyCollection', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const {collectionAddress, collectionId} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
-    const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
 
     await expect(collectionHelper.methods
       .destroyCollection(collectionAddress)
modifiedtests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth
before · tests/src/eth/util/playgrounds/unique.dev.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034/* eslint-disable function-call-argument-newline */5// eslint-disable-next-line @typescript-eslint/triple-slash-reference6/// <reference path="unique.dev.d.ts" />78import {readFile} from 'fs/promises';910import Web3 from 'web3';11import {WebsocketProvider} from 'web3-core';12import {Contract} from 'web3-eth-contract';1314import solc from 'solc';1516import {evmToAddress} from '@polkadot/util-crypto';17import {IKeyringPair} from '@polkadot/types/types';1819import {ArrangeGroup, DevUniqueHelper} from '../../../util/playgrounds/unique.dev';2021import {ContractImports, CompiledContract, CrossAddress, NormalizedEvent, EthProperty, CollectionMode, CreateCollectionData} from './types';2223// Native contracts ABI24import collectionHelpersAbi from '../../abi/collectionHelpers.json' assert {type: 'json'};25import nativeFungibleAbi from '../../abi/nativeFungible.json' assert {type: 'json'};26import fungibleAbi from '../../abi/fungible.json' assert {type: 'json'};27import fungibleDeprecatedAbi from '../../abi/fungibleDeprecated.json' assert {type: 'json'};28import nonFungibleAbi from '../../abi/nonFungible.json' assert {type: 'json'};29import nonFungibleDeprecatedAbi from '../../abi/nonFungibleDeprecated.json' assert {type: 'json'};30import refungibleAbi from '../../abi/reFungible.json' assert {type: 'json'};31import refungibleDeprecatedAbi from '../../abi/reFungibleDeprecated.json' assert {type: 'json'};32import refungibleTokenAbi from '../../abi/reFungibleToken.json' assert {type: 'json'};33import refungibleTokenDeprecatedAbi from '../../abi/reFungibleTokenDeprecated.json' assert {type: 'json'};34import contractHelpersAbi from '../../abi/contractHelpers.json' assert {type: 'json'};35import {ICrossAccountId, TEthereumAccount} from '../../../util/playgrounds/types';36import {TCollectionMode} from '../../../util/playgrounds/types';3738class EthGroupBase {39  helper: EthUniqueHelper;40  gasPrice?: string;4142  constructor(helper: EthUniqueHelper) {43    this.helper = helper;44  }45  async getGasPrice() {46    if(this.gasPrice)47      return this.gasPrice;48    this.gasPrice = await this.helper.getWeb3().eth.getGasPrice();49    return this.gasPrice;50  }51}5253class ContractGroup extends EthGroupBase {54  async findImports(imports?: ContractImports[]) {55    if(!imports) return function(path: string) {56      return {error: `File not found: ${path}`};57    };5859    const knownImports = {} as { [key: string]: string };60    for(const imp of imports) {61      knownImports[imp.solPath] = (await readFile(imp.fsPath)).toString();62    }6364    return function(path: string) {65      if(path in knownImports) return {contents: knownImports[path]};66      return {error: `File not found: ${path}`};67    };68  }6970  async compile(name: string, src: string, imports?: ContractImports[]): Promise<CompiledContract> {71    const compiled = JSON.parse(solc.compile(JSON.stringify({72      language: 'Solidity',73      sources: {74        [`${name}.sol`]: {75          content: src,76        },77      },78      settings: {79        outputSelection: {80          '*': {81            '*': ['*'],82          },83        },84      },85    }), {import: await this.findImports(imports)}));8687    const hasErrors = compiled['errors']88      && compiled['errors'].length > 089      && compiled.errors.some(function(err: any) {90        return err.severity == 'error';91      });9293    if(hasErrors) {94      throw compiled.errors;95    }96    const out = compiled.contracts[`${name}.sol`][name];9798    return {99      abi: out.abi,100      object: '0x' + out.evm.bytecode.object,101    };102  }103104  async deployByCode(signer: string, name: string, src: string, imports?: ContractImports[], gas?: number, args?: any[]): Promise<Contract> {105    const compiledContract = await this.compile(name, src, imports);106    return this.deployByAbi(signer, compiledContract.abi, compiledContract.object, gas, args);107  }108109  async deployByAbi(signer: string, abi: any, object: string, gas?: number, args?: any[]): Promise<Contract> {110    const web3 = this.helper.getWeb3();111    const contract = new web3.eth.Contract(abi, undefined, {112      data: object,113      from: signer,114      gas: gas ?? this.helper.eth.DEFAULT_GAS,115    });116    return await contract.deploy({data: object, arguments: args}).send({from: signer});117  }118119}120121class NativeContractGroup extends EthGroupBase {122123  contractHelpers(caller: string) {124    const web3 = this.helper.getWeb3();125    return new web3.eth.Contract(contractHelpersAbi as any, this.helper.getApi().consts.evmContractHelpers.contractAddress.toString(), {126      from: caller,127      gas: this.helper.eth.DEFAULT_GAS,128    });129  }130131  collectionHelpers(caller: string) {132    const web3 = this.helper.getWeb3();133    return new web3.eth.Contract(collectionHelpersAbi as any, this.helper.getApi().consts.common.contractAddress.toString(), {134      from: caller,135      gas: this.helper.eth.DEFAULT_GAS,136    });137  }138139  collection(address: string, mode: TCollectionMode, caller?: string, mergeDeprecated = false) {140    let abi;141    if(address === this.helper.ethAddress.fromCollectionId(0)) {142      abi = nativeFungibleAbi;143    } else {144      abi ={145        'nft': nonFungibleAbi,146        'rft': refungibleAbi,147        'ft': fungibleAbi,148      }[mode];149    }150    if(mergeDeprecated) {151      const deprecated = {152        'nft': nonFungibleDeprecatedAbi,153        'rft': refungibleDeprecatedAbi,154        'ft': fungibleDeprecatedAbi,155      }[mode];156      abi = [...abi, ...deprecated];157    }158    const web3 = this.helper.getWeb3();159    return new web3.eth.Contract(abi as any, address, {160      gas: this.helper.eth.DEFAULT_GAS,161      ...(caller ? {from: caller} : {}),162    });163  }164165  collectionById(collectionId: number, mode: 'nft' | 'rft' | 'ft', caller?: string, mergeDeprecated = false) {166    return this.collection(this.helper.ethAddress.fromCollectionId(collectionId), mode, caller, mergeDeprecated);167  }168169  rftToken(address: string, caller?: string, mergeDeprecated = false) {170    const web3 = this.helper.getWeb3();171    const abi = mergeDeprecated ? [...refungibleTokenAbi, ...refungibleTokenDeprecatedAbi] : refungibleTokenAbi;172    return new web3.eth.Contract(abi as any, address, {173      gas: this.helper.eth.DEFAULT_GAS,174      ...(caller ? {from: caller} : {}),175    });176  }177178  rftTokenById(collectionId: number, tokenId: number, caller?: string, mergeDeprecated = false) {179    return this.rftToken(this.helper.ethAddress.fromTokenId(collectionId, tokenId), caller, mergeDeprecated);180  }181}182183class CreateCollectionTransaction {184  signer: string;185  data: CreateCollectionData;186  mergeDeprecated: boolean;187  helper: EthUniqueHelper;188189  constructor(helper: EthUniqueHelper, signer: string, data: CreateCollectionData, mergeDeprecated = false) {190    this.helper = helper;191    this.signer = signer;192193    let flags = 0;194    // convert CollectionFlags to number and join them in one number195    if(!data.flags || typeof data.flags == 'number') {196      flags = data.flags ?? 0;197    } else {198      for(let i = 0; i < data.flags.length; i++){199        const flag = data.flags[i];200        flags = flags | flag;201      }202    }203    data.flags = flags;204205    this.data = data;206    this.mergeDeprecated = mergeDeprecated;207  }208209  private async createTransaction() {210    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(this.signer);211    let collectionMode;212    switch (this.data.collectionMode) {213      case 'nft': collectionMode = CollectionMode.Nonfungible; break;214      case 'rft': collectionMode = CollectionMode.Refungible; break;215      case 'ft': collectionMode = CollectionMode.Fungible; break;216    }217218    const tx = collectionHelper.methods.createCollection([219      this.data.name,220      this.data.description,221      this.data.tokenPrefix,222      collectionMode,223      this.data.decimals,224      this.data.properties,225      this.data.tokenPropertyPermissions,226      this.data.adminList,227      this.data.nestingSettings,228      this.data.limits,229      this.data.pendingSponsor,230      this.data.flags,231    ]);232    return tx;233  }234235  async send(options?: any): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[], collection: Contract }> {236    const collectionCreationPrice = {237      value: Number(this.helper.balance.getCollectionCreationPrice()),238    };239    const tx = await this.createTransaction();240    const result = await tx.send({...options, ...collectionCreationPrice});241242    const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);243    const collectionId = this.helper.ethAddress.extractCollectionId(collectionAddress);244    const events = this.helper.eth.normalizeEvents(result.events);245    const collection = await this.helper.ethNativeContract.collectionById(collectionId, this.data.collectionMode, this.signer, this.mergeDeprecated);246247    return {collectionId, collectionAddress, events, collection};248  }249250  async call(options?: any) {251    const collectionCreationPrice = {252      value: Number(this.helper.balance.getCollectionCreationPrice()),253    };254    const tx = await this.createTransaction();255256    return await tx.call({...options, ...collectionCreationPrice});257  }258}259260261class EthGroup extends EthGroupBase {262  DEFAULT_GAS = 2_500_000;263264  createAccount() {265    const web3 = this.helper.getWeb3();266    const account = web3.eth.accounts.create();267    web3.eth.accounts.wallet.add(account.privateKey);268    return account.address;269  }270271  async createAccountWithBalance(donor: IKeyringPair, amount = 600n) {272    const account = this.createAccount();273    await this.transferBalanceFromSubstrate(donor, account, amount);274275    return account;276  }277278  async transferBalanceFromSubstrate(donor: IKeyringPair, recepient: string, amount = 100n, inTokens = true) {279    return await this.helper.balance.transferToSubstrate(donor, evmToAddress(recepient), amount * (inTokens ? this.helper.balance.getOneTokenNominal() : 1n));280  }281282  async getCollectionCreationFee(signer: string) {283    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);284    return await collectionHelper.methods.collectionCreationFee().call();285  }286287  async sendEVM(signer: IKeyringPair, contractAddress: string, abi: string, value: string, gasLimit?: number) {288    if(!gasLimit) gasLimit = this.DEFAULT_GAS;289    const web3 = this.helper.getWeb3();290    // FIXME: can't send legacy transaction using tx.evm.call291    const gasPrice = await web3.eth.getGasPrice();292    // TODO: check execution status293    await this.helper.executeExtrinsic(294      signer,295      'api.tx.evm.call', [this.helper.address.substrateToEth(signer.address), contractAddress, abi, value, gasLimit, gasPrice, null, null, []],296      true,297    );298  }299300  async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) {301    return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);302  }303304  createCollectionMethodName(mode: TCollectionMode) {305    switch (mode) {306      case 'ft':307        return 'createFTCollection';308      case 'nft':309        return 'createNFTCollection';310      case 'rft':311        return 'createRFTCollection';312    }313  }314315  createCollection(signer: string, data: CreateCollectionData, mergeDeprecated = false): CreateCollectionTransaction {316    return new CreateCollectionTransaction(this.helper, signer, data, mergeDeprecated);317  }318319  createNFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {320    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send();321  }322323  async createERC721MetadataCompatibleCollection(signer: string, data: CreateCollectionData, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {324    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);325326    const {collectionId, collectionAddress, events} = await this.createCollection(signer, data).send();327328    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();329330    return {collectionId, collectionAddress, events};331  }332333  async createERC721MetadataCompatibleNFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {334    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);335336    const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'nft')).send();337338    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();339340    return {collectionId, collectionAddress, events};341  }342343  createRFTCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {344    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send();345  }346347  createFungibleCollection(signer: string, name: string, decimals: number, description: string, tokenPrefix: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {348    return this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'ft')).send();349  }350351  async createERC721MetadataCompatibleRFTCollection(signer: string, name: string, description: string, tokenPrefix: string, baseUri: string): Promise<{ collectionId: number, collectionAddress: string, events: NormalizedEvent[] }> {352    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(signer);353354    const {collectionId, collectionAddress, events} = await this.createCollection(signer, new CreateCollectionData(name, description, tokenPrefix, 'rft')).send();355356    await collectionHelper.methods.makeCollectionERC721MetadataCompatible(collectionAddress, baseUri).send();357358    return {collectionId, collectionAddress, events};359  }360361  async deployCollectorContract(signer: string): Promise<Contract> {362    return await this.helper.ethContract.deployByCode(signer, 'Collector', `363    // SPDX-License-Identifier: UNLICENSED364    pragma solidity ^0.8.6;365366    contract Collector {367      uint256 collected;368      fallback() external payable {369        giveMoney();370      }371      function giveMoney() public payable {372        collected += msg.value;373      }374      function getCollected() public view returns (uint256) {375        return collected;376      }377      function getUnaccounted() public view returns (uint256) {378        return address(this).balance - collected;379      }380381      function withdraw(address payable target) public {382        target.transfer(collected);383        collected = 0;384      }385    }386  `);387  }388389  async deployFlipper(signer: string): Promise<Contract> {390    return await this.helper.ethContract.deployByCode(signer, 'Flipper', `391    // SPDX-License-Identifier: UNLICENSED392    pragma solidity ^0.8.6;393394    contract Flipper {395      bool value = false;396      function flip() public {397        value = !value;398      }399      function getValue() public view returns (bool) {400        return value;401      }402    }403  `);404  }405406  async recordCallFee(user: string, call: () => Promise<any>): Promise<bigint> {407    const before = await this.helper.balance.getEthereum(user);408    await call();409    // In dev mode, the transaction might not finish processing in time410    await this.helper.wait.newBlocks(1);411    const after = await this.helper.balance.getEthereum(user);412413    return before - after;414  }415416  normalizeEvents(events: any): NormalizedEvent[] {417    const output = [];418    for(const key of Object.keys(events)) {419      if(key.match(/^[0-9]+$/)) {420        output.push(events[key]);421      } else if(Array.isArray(events[key])) {422        output.push(...events[key]);423      } else {424        output.push(events[key]);425      }426    }427    output.sort((a, b) => a.logIndex - b.logIndex);428    return output.map(({address, event, returnValues}) => {429      const args: { [key: string]: string } = {};430      for(const key of Object.keys(returnValues)) {431        if(!key.match(/^[0-9]+$/)) {432          args[key] = returnValues[key];433        }434      }435      return {436        address,437        event,438        args,439      };440    });441  }442443  async calculateFee(address: ICrossAccountId, code: () => Promise<any>): Promise<bigint> {444    const wrappedCode = async () => {445      await code();446      // In dev mode, the transaction might not finish processing in time447      await this.helper.wait.newBlocks(1);448    };449    return await this.helper.arrange.calculcateFee(address, wrappedCode);450  }451}452453class EthAddressGroup extends EthGroupBase {454  extractCollectionId(address: string): number {455    if(!(address.length === 42 || address.length === 40)) throw new Error('address wrong format');456    return parseInt(address.slice(address.length - 8), 16);457  }458459  fromCollectionId(collectionId: number): string {460    if(collectionId >= 0xffffffff || collectionId < 0) throw new Error('collectionId overflow');461    return Web3.utils.toChecksumAddress(`0x17c4e6453cc49aaaaeaca894e6d9683e${collectionId.toString(16).padStart(8, '0')}`);462  }463464  extractTokenId(address: string): { collectionId: number, tokenId: number } {465    if(!address.startsWith('0x'))466      throw 'address not starts with "0x"';467    if(address.length > 42)468      throw 'address length is more than 20 bytes';469    return {470      collectionId: Number('0x' + address.substring(address.length - 16, address.length - 8)),471      tokenId: Number('0x' + address.substring(address.length - 8)),472    };473  }474475  fromTokenId(collectionId: number, tokenId: number): string {476    return this.helper.util.getTokenAddress({collectionId, tokenId});477  }478479  normalizeAddress(address: string): string {480    return '0x' + address.substring(address.length - 40);481  }482}483export class EthPropertyGroup extends EthGroupBase {484  property(key: string, value: string): EthProperty {485    return [486      key,487      '0x' + Buffer.from(value).toString('hex'),488    ];489  }490}491export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper;492493export class EthCrossAccountGroup extends EthGroupBase {494  createAccount(): CrossAddress {495    return this.fromAddress(this.helper.eth.createAccount());496  }497498  async createAccountWithBalance(donor: IKeyringPair, amount = 100n) {499    return this.fromAddress(await this.helper.eth.createAccountWithBalance(donor, amount));500  }501502  fromAddress(address: TEthereumAccount): CrossAddress {503    return {504      eth: address,505      sub: '0',506    };507  }508509  fromAddr(address: TEthereumAccount): [string, string] {510    return [511      address,512      '0',513    ];514  }515516  fromKeyringPair(keyring: IKeyringPair): CrossAddress {517    return {518      eth: '0x0000000000000000000000000000000000000000',519      sub: keyring.addressRaw,520    };521  }522}523524export class FeeGas {525  fee: number | bigint = 0n;526527  gas: number | bigint = 0n;528529  public static async build(helper: EthUniqueHelper, fee: bigint): Promise<FeeGas> {530    const instance = new FeeGas();531    instance.fee = instance.convertToTokens(fee);532    instance.gas = await instance.convertToGas(fee, helper);533    return instance;534  }535536  private async convertToGas(fee: bigint, helper: EthUniqueHelper): Promise<bigint> {537    const gasPrice = BigInt(await helper.getWeb3().eth.getGasPrice());538    return fee / gasPrice;539  }540541  private convertToTokens(value: bigint, nominal = 1_000_000_000_000_000_000n): number {542    return Number((value * 1000n) / nominal) / 1000;543  }544}545546class EthArrangeGroup extends ArrangeGroup {547  helper: EthUniqueHelper;548549  constructor(helper: EthUniqueHelper) {550    super(helper);551    this.helper = helper;552  }553554  async calculcateFeeGas(payer: ICrossAccountId, promise: () => Promise<any>): Promise<FeeGas> {555    const fee = await this.calculcateFee(payer, promise);556    return await FeeGas.build(this.helper, fee);557  }558}559export class EthUniqueHelper extends DevUniqueHelper {560  web3: Web3 | null = null;561  web3Provider: WebsocketProvider | null = null;562563  eth: EthGroup;564  ethAddress: EthAddressGroup;565  ethCrossAccount: EthCrossAccountGroup;566  ethNativeContract: NativeContractGroup;567  ethContract: ContractGroup;568  ethProperty: EthPropertyGroup;569  arrange: EthArrangeGroup;570  constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: { [key: string]: any } = {}) {571    options.helperBase = options.helperBase ?? EthUniqueHelper;572573    super(logger, options);574    this.eth = new EthGroup(this);575    this.ethAddress = new EthAddressGroup(this);576    this.ethCrossAccount = new EthCrossAccountGroup(this);577    this.ethNativeContract = new NativeContractGroup(this);578    this.ethContract = new ContractGroup(this);579    this.ethProperty = new EthPropertyGroup(this);580    this.arrange = new EthArrangeGroup(this);581    super.arrange = this.arrange;582  }583584  getWeb3(): Web3 {585    if(this.web3 === null) throw Error('Web3 not connected');586    return this.web3;587  }588589  connectWeb3(wsEndpoint: string) {590    if(this.web3 !== null) return;591    this.web3Provider = new Web3.providers.WebsocketProvider(wsEndpoint);592    this.web3 = new Web3(this.web3Provider);593  }594595  async disconnect() {596    if(this.web3 === null) return;597    this.web3Provider?.connection.close();598599    await super.disconnect();600  }601602  clearApi() {603    super.clearApi();604    this.web3 = null;605  }606607  clone(helperCls: EthUniqueHelperConstructor, options?: { [key: string]: any; }): EthUniqueHelper {608    const newHelper = super.clone(helperCls, options) as EthUniqueHelper;609    newHelper.web3 = this.web3;610    newHelper.web3Provider = this.web3Provider;611612    return newHelper;613  }614}
modifiedtests/src/util/index.tsdiffbeforeafterboth
--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -15,6 +15,7 @@
 import {dirname} from 'path';
 import {fileURLToPath} from 'url';
 
+chai.config.truncateThreshold = 0;
 chai.use(chaiAsPromised);
 chai.use(chaiSubset);
 export const expect = chai.expect;
modifiedtests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -41,7 +41,9 @@
       for(const arg of args) {
         if(typeof arg !== 'string')
           continue;
-        if(arg.includes('1000:: Normal connection closure') || arg.includes('Not decorating unknown runtime apis:') || arg.includes('RPC methods not decorated:') || arg === 'Normal connection closure')
+        const skippedWarnings = ['1000:: Normal connection closure', 'Not decorating unknown runtime apis:', 'RPC methods not decorated:', 'Not decorating runtime apis'];
+        const needToSkip = skippedWarnings.reduce((a,  b) => a || arg.includes(b), false);
+        if(needToSkip || arg === 'Normal connection closure')
           return;
       }
       printer(...args);