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
before · tests/src/eth/createCollection.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.8//9// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {evmToAddress} from '@polkadot/util-crypto';19import {Pallets, requirePalletsOrSkip} from '../util';20import {expect, itEth, usingEthPlaygrounds} from './util';21import {CREATE_COLLECTION_DATA_DEFAULTS, CollectionLimitField, CollectionMode, CreateCollectionData, TokenPermissionField, emptyAddress} from './util/playgrounds/types';22import {CollectionFlag, IEthCrossAccountId, TCollectionMode} from '../util/playgrounds/types';2324const DECIMALS = 18;25const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [26  [],27  [],28  [],29  [false, false, []],30  [],31  [0],32];3334type ElementOf<A> = A extends readonly (infer T)[] ? T : never;35function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {36  if(args.length === 0) {37    yield internalRest as any;38    return;39  }40  for(const value of args[0]) {41    yield* cartesian([...internalRest, value], ...args.slice(1)) as any;42  }43}4445describe('Create collection from EVM', () => {46  let donor: IKeyringPair;47  let nominal: bigint;4849  before(async function() {50    await usingEthPlaygrounds(async (helper, privateKey) => {51      donor = await privateKey({url: import.meta.url});52      nominal = helper.balance.getOneTokenNominal();53    });54  });5556  describe('Fungible collection', () => {57    before(async function() {58      await usingEthPlaygrounds((helper) => {59        requirePalletsOrSkip(this, helper, [Pallets.Fungible]);60        return Promise.resolve();61      });62    });6364    itEth('Collection address exist', async ({helper}) => {65      const owner = await helper.eth.createAccountWithBalance(donor);66      const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';67      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);6869      expect(await collectionHelpers70        .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())71        .to.be.false;7273      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();74      expect(await collectionHelpers75        .methods.isCollectionExist(collectionAddress).call())76        .to.be.true;7778      // check collectionOwner:79      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);80      const collectionOwner = await collectionEvm.methods.collectionOwner().call();81      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));82    });8384    itEth('destroyCollection', async ({helper}) => {85      const owner = await helper.eth.createAccountWithBalance(donor);86      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();87      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);8889      const result = await collectionHelper.methods90        .destroyCollection(collectionAddress)91        .send({from: owner});9293      const events = helper.eth.normalizeEvents(result.events);9495      expect(events).to.be.deep.equal([96        {97          address: collectionHelper.options.address,98          event: 'CollectionDestroyed',99          args: {100            collectionId: collectionAddress,101          },102        },103      ]);104105      expect(await collectionHelper.methods106        .isCollectionExist(collectionAddress)107        .call()).to.be.false;108      expect(await helper.collection.getData(collectionId)).to.be.null;109    });110111    itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {112      const owner = await helper.eth.createAccountWithBalance(donor);113      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);114      {115        const MAX_NAME_LENGTH = 64;116        const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);117        const description = 'A';118        const tokenPrefix = 'A';119120        await expect(collectionHelper.methods121          .createCollection([122            emptyAddress,123            collectionName,124            description,125            tokenPrefix,126            CollectionMode.Fungible,127            DECIMALS,128            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,129          ])130          .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);131      }132      {133        const MAX_DESCRIPTION_LENGTH = 256;134        const collectionName = 'A';135        const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);136        const tokenPrefix = 'A';137        await expect(collectionHelper.methods138          .createCollection([139            emptyAddress,140            collectionName,141            description,142            tokenPrefix,143            CollectionMode.Fungible,144            DECIMALS,145            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,146          ])147          .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);148      }149      {150        const MAX_TOKEN_PREFIX_LENGTH = 16;151        const collectionName = 'A';152        const description = 'A';153        const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);154        await expect(collectionHelper.methods155          .createCollection([156            emptyAddress,157            collectionName,158            description,159            tokenPrefix,160            CollectionMode.Fungible,161            DECIMALS,162            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,163          ])164          .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);165      }166    });167168    itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {169      const owner = await helper.eth.createAccountWithBalance(donor);170      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);171      const expects = [0n, 1n, 30n].map(async value => {172        await expect(collectionHelper.methods173          .createCollection([174            emptyAddress,175            'Peasantry',176            'absolutely anything',177            'TWIW',178            CollectionMode.Fungible,179            DECIMALS,180            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,181          ])182          .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');183      });184      await Promise.all(expects);185    });186  });187188  describe('Nonfungible collection', () => {189    before(async function() {190      await usingEthPlaygrounds((helper) => {191        requirePalletsOrSkip(this, helper, [Pallets.NFT]);192        return Promise.resolve();193      });194    });195196    itEth('Create collection', async ({helper}) => {197      const owner = await helper.eth.createAccountWithBalance(donor);198199      const name = 'CollectionEVM';200      const description = 'Some description';201      const prefix = 'token prefix';202203      // todo:playgrounds this might fail when in async environment.204      const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;205      const {collectionId, collectionAddress, events} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'nft')).send();206207      expect(events).to.be.deep.equal([208        {209          address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',210          event: 'CollectionCreated',211          args: {212            owner: owner,213            collectionId: collectionAddress,214          },215        },216      ]);217218      const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;219220      const collection = helper.nft.getCollectionObject(collectionId);221      const data = (await collection.getData())!;222223      expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);224      expect(collectionId).to.be.eq(collectionCountAfter);225      expect(data.name).to.be.eq(name);226      expect(data.description).to.be.eq(description);227      expect(data.raw.tokenPrefix).to.be.eq(prefix);228      expect(data.raw.mode).to.be.eq('NFT');229230      const options = await collection.getOptions();231232      expect(options.tokenPropertyPermissions).to.be.empty;233    });234235    // this test will occasionally fail when in async environment.236    itEth('Check collection address exist', async ({helper}) => {237      const owner = await helper.eth.createAccountWithBalance(donor);238239      const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;240      const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);241      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);242243      expect(await collectionHelpers.methods244        .isCollectionExist(expectedCollectionAddress)245        .call()).to.be.false;246247      await collectionHelpers.methods248        .createCollection([249          emptyAddress,250          'A',251          'A',252          'A',253          CollectionMode.Nonfungible,254          0,255          ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,256        ])257        .send({value: Number(2n * helper.balance.getOneTokenNominal())});258259      expect(await collectionHelpers.methods260        .isCollectionExist(expectedCollectionAddress)261        .call()).to.be.true;262    });263  });264265  describe('Create RFT collection from EVM', () => {266    before(async function() {267      await usingEthPlaygrounds((helper) => {268        requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);269        return Promise.resolve();270      });271    });272273    itEth('Create collection', async ({helper}) => {274      const owner = await helper.eth.createAccountWithBalance(donor);275276      const name = 'CollectionEVM';277      const description = 'Some description';278      const prefix = 'token prefix';279280      const {collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'rft')).send();281      const data = (await helper.rft.getData(collectionId))!;282      const collection = helper.rft.getCollectionObject(collectionId);283284      expect(data.name).to.be.eq(name);285      expect(data.description).to.be.eq(description);286      expect(data.raw.tokenPrefix).to.be.eq(prefix);287      expect(data.raw.mode).to.be.eq('ReFungible');288289      const options = await collection.getOptions();290291      expect(options.tokenPropertyPermissions).to.be.empty;292    });293294    itEth('Create collection with properties & get description', async ({helper}) => {295      const owner = await helper.eth.createAccountWithBalance(donor);296297      const name = 'CollectionEVM';298      const description = 'Some description';299      const prefix = 'token prefix';300      const baseUri = 'BaseURI';301302      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);303      const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');304305      const collection = helper.rft.getCollectionObject(collectionId);306      const data = (await collection.getData())!;307308      expect(data.name).to.be.eq(name);309      expect(data.description).to.be.eq(description);310      expect(data.raw.tokenPrefix).to.be.eq(prefix);311      expect(data.raw.mode).to.be.eq('ReFungible');312313      expect(await contract.methods.description().call()).to.deep.equal(description);314315      const options = await collection.getOptions();316      expect(options.tokenPropertyPermissions).to.be.deep.equal([317        {318          key: 'URI',319          permission: {mutable: true, collectionAdmin: true, tokenOwner: false},320        },321        {322          key: 'URISuffix',323          permission: {mutable: true, collectionAdmin: true, tokenOwner: false},324        },325      ]);326    });327328    itEth('Set sponsorship', async ({helper}) => {329      const owner = await helper.eth.createAccountWithBalance(donor);330      const sponsor = await helper.eth.createAccountWithBalance(donor);331      const ss58Format = helper.chain.getChainProperties().ss58Format;332      const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Sponsor', 'absolutely anything', 'ENVY', 'rft')).send();333334      const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);335      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);336      await collection.methods.setCollectionSponsorCross(sponsorCross).send();337338      let data = (await helper.rft.getData(collectionId))!;339      expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));340341      await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');342343      const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);344      await sponsorCollection.methods.confirmCollectionSponsorship().send();345346      data = (await helper.rft.getData(collectionId))!;347      expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));348    });349350    itEth('Collection address exist', async ({helper}) => {351      const owner = await helper.eth.createAccountWithBalance(donor);352      const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';353      const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);354355      expect(await collectionHelpers356        .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())357        .to.be.false;358359      const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'rft')).send();360      expect(await collectionHelpers361        .methods.isCollectionExist(collectionAddress).call())362        .to.be.true;363364      // check collectionOwner:365      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);366      const collectionOwner = await collectionEvm.methods.collectionOwner().call();367      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));368    });369370    itEth('destroyCollection', async ({helper}) => {371      const owner = await helper.eth.createAccountWithBalance(donor);372      const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'OLF', 'rft')).send();373      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);374375      await expect(collectionHelper.methods376        .destroyCollection(collectionAddress)377        .send({from: owner})).to.be.fulfilled;378379      expect(await collectionHelper.methods380        .isCollectionExist(collectionAddress)381        .call()).to.be.false;382      expect(await helper.collection.getData(collectionId)).to.be.null;383    });384385    itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {386      const owner = await helper.eth.createAccountWithBalance(donor);387      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);388      {389        const MAX_NAME_LENGTH = 64;390        const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);391        const description = 'A';392        const tokenPrefix = 'A';393394        await expect(collectionHelper.methods395          .createCollection([396            emptyAddress,397            collectionName,398            description,399            tokenPrefix,400            CollectionMode.Refungible,401            DECIMALS,402            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,403          ])404          .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);405      }406      {407        const MAX_DESCRIPTION_LENGTH = 256;408        const collectionName = 'A';409        const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);410        const tokenPrefix = 'A';411        await expect(collectionHelper.methods412          .createCollection([413            emptyAddress,414            collectionName,415            description,416            tokenPrefix,417            CollectionMode.Refungible,418            DECIMALS,419            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,420          ])421          .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);422      }423      {424        const MAX_TOKEN_PREFIX_LENGTH = 16;425        const collectionName = 'A';426        const description = 'A';427        const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);428        await expect(collectionHelper.methods429          .createCollection([430            emptyAddress,431            collectionName,432            description,433            tokenPrefix,434            CollectionMode.Refungible,435            DECIMALS,436            ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,437          ])438          .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);439      }440    });441442    itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {443      const owner = await helper.eth.createAccountWithBalance(donor);444      const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);445      await expect(collectionHelper.methods446        .createCollection([447          emptyAddress,448          'Peasantry',449          'absolutely anything',450          'TWIW',451          CollectionMode.Refungible,452          0,453          ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,454        ])455        .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');456    });457  });458459  describe('Sponsoring', () => {460    itEth('Сan remove collection sponsor', async ({helper}) => {461      const owner = await helper.eth.createAccountWithBalance(donor);462      const sponsor = await helper.eth.createAccountWithBalance(donor);463      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);464465      const {collectionAddress} = await helper.eth.createCollection(466        owner,467        {468          ...CREATE_COLLECTION_DATA_DEFAULTS,469          name: 'Sponsor collection',470          description: '1',471          tokenPrefix: '1',472          collectionMode: 'nft',473          pendingSponsor: sponsorCross,474        },475      ).send();476      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);477478      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;479480      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});481      let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});482      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));483      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;484485      await collectionEvm.methods.removeCollectionSponsor().send({from: owner});486487      sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});488      expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');489    });490491    itEth('Can sponsor from evm address via access list', async ({helper}) => {492      const owner = await helper.eth.createAccountWithBalance(donor);493      const sponsorEth = await helper.eth.createAccountWithBalance(donor);494      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsorEth);495496      const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(497        owner,498        {499          ...CREATE_COLLECTION_DATA_DEFAULTS,500          name: 'Sponsor collection',501          description: '1',502          tokenPrefix: '1',503          collectionMode: 'nft',504          pendingSponsor: sponsorCross,505          limits: [{field: CollectionLimitField.SponsoredDataRateLimit, value: 30n}],506          tokenPropertyPermissions: [{key: 'key', permissions: [{code: TokenPermissionField.TokenOwner, value: true}]}],507        },508        '',509      );510511      const collectionSub = helper.nft.getCollectionObject(collectionId);512      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);513514      let sponsorship = (await collectionSub.getData())!.raw.sponsorship;515      expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));516      // Account cannot confirm sponsorship if it is not set as a sponsor517      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');518519      // Sponsor can confirm sponsorship:520      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});521      sponsorship = (await collectionSub.getData())!.raw.sponsorship;522      expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));523524      // Create user with no balance:525      const user = helper.ethCrossAccount.createAccount();526      const nextTokenId = await collectionEvm.methods.nextTokenId().call();527      expect(nextTokenId).to.be.equal('1');528529      // Set collection permissions:530      const oldPermissions = (await collectionSub.getData())!.raw.permissions;531      expect(oldPermissions.mintMode).to.be.false;532      expect(oldPermissions.access).to.be.equal('Normal');533534      await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});535      await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});536      await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});537538      const newPermissions = (await collectionSub.getData())!.raw.permissions;539      expect(newPermissions.mintMode).to.be.true;540      expect(newPermissions.access).to.be.equal('AllowList');541542      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));543      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));544      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));545546      // User can mint token without balance:547      {548        const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});549        const event = helper.eth.normalizeEvents(result.events)550          .find(event => event.event === 'Transfer');551552        expect(event).to.be.deep.equal({553          address: collectionAddress,554          event: 'Transfer',555          args: {556            from: '0x0000000000000000000000000000000000000000',557            to: user.eth,558            tokenId: '1',559          },560        });561562        // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth});563564        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));565        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));566        const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));567568        expect(await collectionEvm.methods.properties(nextTokenId, []).call())569          .to.be.like([570            [571              'key',572              '0x' + Buffer.from('Value').toString('hex'),573            ],574          ]);575        expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);576        expect(userBalanceAfter).to.be.eq(userBalanceBefore);577        expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;578      }579    });580581    itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => {582      const owner = await helper.eth.createAccountWithBalance(donor);583      const sponsor = await helper.eth.createAccountWithBalance(donor);584      const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);585      const user = helper.eth.createAccount();586      const userCross = helper.ethCrossAccount.fromAddress(user);587588      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(589        owner,590        {591          ...CREATE_COLLECTION_DATA_DEFAULTS,592          name: 'Sponsor collection',593          description: '1',594          tokenPrefix: '1',595          collectionMode: 'nft',596          pendingSponsor: sponsorCross,597          adminList: [userCross],598        },599        '',600      );601602      const collectionSub = helper.nft.getCollectionObject(collectionId);603      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);604      // Set collection sponsor:605      let collectionData = (await collectionSub.getData())!;606      expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));607      await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');608609      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});610      collectionData = (await collectionSub.getData())!;611      expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));612613      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));614      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));615616      const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});617      const tokenId = mintingResult.events.Transfer.returnValues.tokenId;618619      const event = helper.eth.normalizeEvents(mintingResult.events)620        .find(event => event.event === 'Transfer');621      const address = helper.ethAddress.fromCollectionId(collectionId);622623      expect(event).to.be.deep.equal({624        address,625        event: 'Transfer',626        args: {627          from: '0x0000000000000000000000000000000000000000',628          to: user,629          tokenId: '1',630        },631      });632      expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');633634      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));635      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);636      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));637      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;638    });639640    itEth('Can reassign collection sponsor', async ({helper}) => {641      const owner = await helper.eth.createAccountWithBalance(donor);642      const sponsorEth = await helper.eth.createAccountWithBalance(donor);643      const sponsorCrossEth = helper.ethCrossAccount.fromAddr(sponsorEth);644      const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);645      const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);646647      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(648        owner,649        {650          ...CREATE_COLLECTION_DATA_DEFAULTS,651          name: 'Sponsor collection',652          description: '1',653          tokenPrefix: '1',654          collectionMode: 'nft',655          pendingSponsor: sponsorCrossEth,656        },657        '',658      );659      const collectionSub = helper.nft.getCollectionObject(collectionId);660      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);661662      // Set and confirm sponsor:663      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});664665      // Can reassign sponsor:666      await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});667      const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;668      expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});669    });670671    [672      'transfer',673      'transferCross',674      'transferFrom',675      'transferFromCross',676    ].map(testCase =>677      itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {678        const owner = await helper.eth.createAccountWithBalance(donor);679        const sponsor = await helper.eth.createAccountWithBalance(donor);680        const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);681        const user = await helper.eth.createAccountWithBalance(donor);682        const userCross = helper.ethCrossAccount.fromAddress(user);683684        const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(685          owner,686          {687            ...CREATE_COLLECTION_DATA_DEFAULTS,688            name: 'Sponsor collection',689            description: '1',690            tokenPrefix: '1',691            collectionMode: 'rft',692            pendingSponsor: sponsorCross,693            adminList: [userCross],694          },695          '',696        );697        const receiver = await helper.eth.createAccountWithBalance(donor);698        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);699700        await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});701702        const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});703        const tokenId = result.events.Transfer.returnValues.tokenId;704705        const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));706        const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));707        const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));708709        switch (testCase) {710          case 'transfer':711            await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});712            break;713          case 'transferCross':714            await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});715            break;716          case 'transferFrom':717            await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});718            break;719          case 'transferFromCross':720            await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});721            break;722        }723724        const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));725        expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);726        const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));727        expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;728        const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));729        expect(userBalanceAfter).to.be.eq(userBalanceBefore);730      }));731  });732733  describe('Collection admins', () => {734    let donor: IKeyringPair;735736    before(async function() {737      await usingEthPlaygrounds(async (_helper, privateKey) => {738        donor = await privateKey({url: import.meta.url});739      });740    });741742    [743      {mode: 'nft' as const, requiredPallets: []},744      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},745      {mode: 'ft' as const, requiredPallets: []},746    ].map(testCase => {747      itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {748        // arrange749        const owner = await helper.eth.createAccountWithBalance(donor);750        const adminSub = await privateKey('//admin2');751        const adminEth = helper.eth.createAccount().toLowerCase();752753        const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);754        const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);755756        const {collectionAddress, collectionId} = await helper.eth.createCollection(757          owner,758          {759            ...CREATE_COLLECTION_DATA_DEFAULTS,760            name: 'Mint collection',761            description: 'a',762            tokenPrefix: 'b',763            collectionMode: testCase.mode,764            adminList: [adminCrossSub, adminCrossEth],765          },766        ).send();767        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);768769        // 1. Expect api.rpc.unique.adminlist returns admins:770        const adminListRpc = await helper.collection.getAdmins(collectionId);771        expect(adminListRpc).to.has.length(2);772        expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}]);773774        // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist775        let adminListEth = await collectionEvm.methods.collectionAdmins().call();776        adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));777        expect(adminListRpc).to.be.like(adminListEth);778779        // 3. check isOwnerOrAdminCross returns true:780        expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;781        expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;782      });783    });784785    itEth('cross account admin can mint', async ({helper}) => {786      // arrange: create collection and accounts787      const owner = await helper.eth.createAccountWithBalance(donor);788      const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();789      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);790      const [adminSub] = await helper.arrange.createAccounts([100n], donor);791      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);792      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(793        owner,794        {795          ...CREATE_COLLECTION_DATA_DEFAULTS,796          name: 'Mint collection',797          description: 'a',798          tokenPrefix: 'b',799          collectionMode: 'nft',800          adminList: [adminCrossSub, adminCrossEth],801        },802        'uri',803      );804      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);805806      // admin (sub and eth) can mint token:807      await collectionEvm.methods.mint(owner).send({from: adminEth});808      await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}});809810      expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2);811    });812813    itEth('cannot add invalid cross account admin', async ({helper}) => {814      const owner = await helper.eth.createAccountWithBalance(donor);815      const [admin] = await helper.arrange.createAccounts([100n, 100n], donor);816817      const adminCross = {818        eth: helper.address.substrateToEth(admin.address),819        sub: admin.addressRaw,820      };821822      await expect(helper.eth.createCollection(823        owner,824        {825          ...CREATE_COLLECTION_DATA_DEFAULTS,826          name: 'A',827          description: 'B',828          tokenPrefix: 'C',829          collectionMode: 'nft',830          adminList: [adminCross],831        },832      ).call()).to.be.rejected;833    });834835    itEth('Remove [cross] admin by owner', async ({helper}) => {836      const owner = await helper.eth.createAccountWithBalance(donor);837838      const [adminSub] = await helper.arrange.createAccounts([10n], donor);839      const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();840      const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);841      const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);842843      const {collectionAddress, collectionId} = await helper.eth.createCollection(844        owner,845        {846          ...CREATE_COLLECTION_DATA_DEFAULTS,847          name: 'A',848          description: 'B',849          tokenPrefix: 'C',850          collectionMode: 'nft',851          adminList: [adminCrossSub, adminCrossEth],852        },853      ).send();854855      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);856857      {858        const adminList = await helper.collection.getAdmins(collectionId);859        expect(adminList).to.deep.include({Substrate: adminSub.address});860        expect(adminList).to.deep.include({Ethereum: adminEth});861      }862863      await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send();864      await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send();865      const adminList = await helper.collection.getAdmins(collectionId);866      expect(adminList.length).to.be.eq(0);867868      // Non admin cannot mint:869      await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);870      await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected;871    });872  });873874  describe('Collection limits', () => {875    describe('Can set collection limits', () => {876      [877        {case: 'nft' as const},878        {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},879        {case: 'ft' as const},880      ].map(testCase =>881        itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {882          const owner = await helper.eth.createAccountWithBalance(donor);883884          const limits = {885            accountTokenOwnershipLimit: 1000n,886            sponsoredDataSize: 1024n,887            sponsoredDataRateLimit: 30n,888            tokenLimit: 1000000n,889            sponsorTransferTimeout: 6n,890            sponsorApproveTimeout: 6n,891            ownerCanTransfer: 1n,892            ownerCanDestroy: 0n,893            transfersEnabled: 0n,894          };895896          const {collectionId, collectionAddress} = await helper.eth.createCollection(897            owner,898            {899              ...CREATE_COLLECTION_DATA_DEFAULTS,900              name: 'Limits',901              description: 'absolutely anything',902              tokenPrefix: 'FLO',903              collectionMode: testCase.case,904              limits: [905                {field: CollectionLimitField.AccountTokenOwnership, value: limits.accountTokenOwnershipLimit},906                {field: CollectionLimitField.SponsoredDataSize, value: limits.sponsoredDataSize},907                {field: CollectionLimitField.SponsoredDataRateLimit, value: limits.sponsoredDataRateLimit},908                {field: CollectionLimitField.TokenLimit, value: limits.tokenLimit},909                {field: CollectionLimitField.SponsorTransferTimeout, value: limits.sponsorTransferTimeout},910                {field: CollectionLimitField.SponsorApproveTimeout, value: limits.sponsorApproveTimeout},911                {field: CollectionLimitField.OwnerCanTransfer, value: limits.ownerCanTransfer},912                {field: CollectionLimitField.OwnerCanDestroy, value: limits.ownerCanDestroy},913                {field: CollectionLimitField.TransferEnabled, value: limits.transfersEnabled},914              ],915            },916          ).send();917918          const expectedLimits = {919            accountTokenOwnershipLimit: 1000,920            sponsoredDataSize: 1024,921            sponsoredDataRateLimit: {blocks: 30},922            tokenLimit: 1000000,923            sponsorTransferTimeout: 6,924            sponsorApproveTimeout: 6,925            ownerCanTransfer: true,926            ownerCanDestroy: false,927            transfersEnabled: false,928          };929930          const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);931932          // Check limits from sub:933          const data = (await helper.rft.getData(collectionId))!;934          expect(data.raw.limits).to.deep.eq(expectedLimits);935          expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits);936          // Check limits from eth:937          const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner});938          expect(limitsEvm).to.have.length(9);939          expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]);940          expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]);941          expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]);942          expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]);943          expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]);944          expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]);945          expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]);946          expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]);947          expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]);948        }));949    });950951    describe('(!negative test!) Cannot set invalid collection limits', () => {952      [953        {case: 'nft' as const},954        {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},955        {case: 'ft' as const},956      ].map(testCase =>957        itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {958          const invalidLimits = {959            accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER),960            transfersEnabled: 3,961          };962963          const createCollectionData = {964            ...CREATE_COLLECTION_DATA_DEFAULTS,965            name: 'Limits',966            description: 'absolutely anything',967            tokenPrefix: 'ISNI',968            collectionMode: testCase.case,969          };970971          const owner = await helper.eth.createAccountWithBalance(donor);972          await expect(helper.eth.createCollection(973            owner,974            {975              ...createCollectionData,976              limits: [{field: 9 as CollectionLimitField, value: 1n}],977            },978          ).call()).to.be.rejectedWith('value not convertible into enum "CollectionLimitField"');979980          await expect(helper.eth.createCollection(981            owner,982            {983              ...createCollectionData,984              limits: [{field: CollectionLimitField.AccountTokenOwnership, value: invalidLimits.accountTokenOwnershipLimit}],985            },986          ).call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`);987988          await expect(helper.eth.createCollection(989            owner,990            {991              ...createCollectionData,992              limits: [{field: CollectionLimitField.TransferEnabled, value: 3n}],993            },994          ).call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`);995996          await expect(helper.eth.createCollection(997            owner,998            {999              ...createCollectionData,1000              limits: [{field: CollectionLimitField.SponsoredDataSize, value: -1n}],1001            },1002          ).call()).to.be.rejectedWith('value out-of-bounds');1003        }));1004    });1005  });10061007  describe('Collection properties', () => {10081009    [1010      {mode: 'nft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1011      {mode: 'rft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1012      {mode: 'ft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1013    ].map(testCase =>1014      itEth.ifWithPallets(`Collection properties can be set for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {1015        const caller = await helper.eth.createAccountWithBalance(donor);1016        const callerCross = helper.ethCrossAccount.fromAddress(caller);1017        const owner = await helper.eth.createAccountWithBalance(donor);1018        const {collectionId, collectionAddress} = await helper.eth.createCollection(1019          owner,1020          {1021            ...CREATE_COLLECTION_DATA_DEFAULTS,1022            name: 'name',1023            description: 'test',1024            tokenPrefix: 'test',1025            collectionMode: testCase.mode,1026            adminList: [callerCross],1027            properties: testCase.methodParams,1028          },1029        ).send();10301031        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);10321033        const raw = (await helper[testCase.mode].getData(collectionId))?.raw;1034        expect(raw.properties).to.deep.equal(testCase.expectedProps);10351036        // collectionProperties returns properties:1037        expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));1038      }));10391040    [1041      {mode: 'nft' as const},1042      {mode: 'rft' as const},1043      {mode: 'ft' as const},1044    ].map(testCase =>1045      itEth.ifWithPallets(`Collection properties can be deleted for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {1046        const caller = await helper.eth.createAccountWithBalance(donor);1047        const callerCross = helper.ethCrossAccount.fromAddress(caller);1048        const owner = await helper.eth.createAccountWithBalance(donor);1049        const {collectionId, collectionAddress} = await helper.eth.createCollection(1050          owner,1051          {1052            ...CREATE_COLLECTION_DATA_DEFAULTS,1053            name: 'name',1054            description: 'test',1055            tokenPrefix: 'test',1056            collectionMode: testCase.mode,1057            adminList: [callerCross],1058            properties:[1059              {key: 'testKey1', value: Buffer.from('testValue1')},1060              {key: 'testKey2', value: Buffer.from('testValue2')},1061              {key: 'testKey3', value: Buffer.from('testValue3')}],1062          },1063        ).send();10641065        const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);10661067        await collectionEvm.methods.deleteCollectionProperties(['testKey1', 'testKey2']).send({from: caller});10681069        const raw = (await helper[testCase.mode].getData(collectionId))?.raw;10701071        expect(raw.properties.length).to.equal(1);1072        expect(raw.properties).to.deep.equal([{key: 'testKey3', value: 'testValue3'}]);1073      }));10741075    itEth('(!negative test!) Cannot set invalid properties', async({helper}) => {1076      const caller = await helper.eth.createAccountWithBalance(donor);1077      const callerCross = helper.ethCrossAccount.fromAddress(caller);1078      const owner = await helper.eth.createAccountWithBalance(donor);1079      const createCollectionData = {1080        ...CREATE_COLLECTION_DATA_DEFAULTS,1081        name: 'name',1082        description: 'test',1083        tokenPrefix: 'test',1084        collectionMode: 'nft' as TCollectionMode,1085        adminList: [callerCross],1086      };1087      await expect(helper.eth.createCollection(1088        owner,1089        {1090          ...createCollectionData,1091          properties: [{key: '', value: Buffer.from('val1')}],1092        },1093      ).call()).to.be.rejected;10941095      await expect(helper.eth.createCollection(1096        owner,1097        {1098          ...createCollectionData,1099          properties: [{key: 'déjà vu', value: Buffer.from('hmm...')}],1100        },1101      ).call()).to.be.rejected;11021103      await expect(helper.eth.createCollection(1104        owner,1105        {1106          ...createCollectionData,1107          properties: [{key: 'a'.repeat(257), value: Buffer.from('val3')}],1108        },1109      ).call()).to.be.rejected;1110    });11111112    itEth('(!negative test!) cannot delete properties of non-owned collections', async ({helper}) => {1113      const caller = await helper.eth.createAccountWithBalance(donor);1114      const owner = await helper.eth.createAccountWithBalance(donor);1115      const {collectionAddress} = await helper.eth.createCollection(1116        owner,1117        {1118          ...CREATE_COLLECTION_DATA_DEFAULTS,1119          name: 'name',1120          description: 'test',1121          tokenPrefix: 'test',1122          collectionMode: 'nft',1123          properties:[1124            {key: 'testKey1', value: Buffer.from('testValue1')},1125            {key: 'testKey2', value: Buffer.from('testValue2')}],1126        },1127      ).send();11281129      const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);11301131      await expect(collectionEvm.methods.deleteCollectionProperties(['testKey2']).send({from: caller})).to.be.rejected;1132    });1133  });11341135  describe('Token property permissions', () => {1136    [1137      {mode: 'nft' as const, requiredPallets: []},1138      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1139    ].map(testCase =>1140      itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {1141        const owner = await helper.eth.createAccountWithBalance(donor);1142        const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);1143        for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {1144          const {collectionId, collectionAddress} = await helper.eth.createCollection(1145            owner,1146            {1147              ...CREATE_COLLECTION_DATA_DEFAULTS,1148              name: 'A',1149              description: 'B',1150              tokenPrefix: 'C',1151              collectionMode: testCase.mode,1152              adminList: [caller],1153              tokenPropertyPermissions: [1154                {1155                  key: 'testKey',1156                  permissions: [1157                    {code: TokenPermissionField.Mutable, value: mutable},1158                    {code: TokenPermissionField.TokenOwner, value: tokenOwner},1159                    {code: TokenPermissionField.CollectionAdmin, value: collectionAdmin},1160                  ],1161                },1162              ],1163            },1164          ).send();1165          const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);11661167          expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{1168            key: 'testKey',1169            permission: {mutable, collectionAdmin, tokenOwner},1170          }]);11711172          expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([1173            ['testKey', [1174              [TokenPermissionField.Mutable.toString(), mutable],1175              [TokenPermissionField.TokenOwner.toString(), tokenOwner],1176              [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]],1177            ],1178          ]);1179        }1180      }));11811182    [1183      {mode: 'nft' as const, requiredPallets: []},1184      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1185    ].map(testCase =>1186      itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions`, testCase.requiredPallets, async({helper}) => {1187        const owner = await helper.eth.createAccountWithBalance(donor);1188        const {collectionId, collectionAddress} = await helper.eth.createCollection(1189          owner,1190          {1191            ...CREATE_COLLECTION_DATA_DEFAULTS,1192            name: 'A',1193            description: 'B',1194            tokenPrefix: 'C',1195            collectionMode: testCase.mode,1196            tokenPropertyPermissions: [1197              {1198                key: 'testKey_0',1199                permissions: [1200                  {code: TokenPermissionField.Mutable, value: true},1201                  {code: TokenPermissionField.TokenOwner, value: true},1202                  {code: TokenPermissionField.CollectionAdmin, value: true}],1203              },1204              {1205                key: 'testKey_1',1206                permissions: [1207                  {code: TokenPermissionField.Mutable, value: true},1208                  {code: TokenPermissionField.TokenOwner, value: false},1209                  {code: TokenPermissionField.CollectionAdmin, value: true}],1210              },1211              {1212                key: 'testKey_2',1213                permissions: [1214                  {code: TokenPermissionField.Mutable, value: false},1215                  {code: TokenPermissionField.TokenOwner, value: true},1216                  {code: TokenPermissionField.CollectionAdmin, value: false}],1217              },1218            ],1219          },1220        ).send();1221        const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);12221223        expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([1224          {1225            key: 'testKey_0',1226            permission: {mutable: true, tokenOwner: true, collectionAdmin: true},1227          },1228          {1229            key: 'testKey_1',1230            permission: {mutable: true, tokenOwner: false, collectionAdmin: true},1231          },1232          {1233            key: 'testKey_2',1234            permission: {mutable: false, tokenOwner: true, collectionAdmin: false},1235          },1236        ]);12371238        expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([1239          ['testKey_0', [1240            [TokenPermissionField.Mutable.toString(), true],1241            [TokenPermissionField.TokenOwner.toString(), true],1242            [TokenPermissionField.CollectionAdmin.toString(), true]],1243          ],1244          ['testKey_1', [1245            [TokenPermissionField.Mutable.toString(), true],1246            [TokenPermissionField.TokenOwner.toString(), false],1247            [TokenPermissionField.CollectionAdmin.toString(), true]],1248          ],1249          ['testKey_2', [1250            [TokenPermissionField.Mutable.toString(), false],1251            [TokenPermissionField.TokenOwner.toString(), true],1252            [TokenPermissionField.CollectionAdmin.toString(), false]],1253          ],1254        ]);1255      }));12561257    [1258      {mode: 'nft' as const, requiredPallets: []},1259      {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1260    ].map(testCase =>1261      itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {1262        const caller = await helper.eth.createAccountWithBalance(donor);1263        const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);1264        const {collectionAddress} = await helper.eth.createCollection(1265          caller,1266          {1267            ...CREATE_COLLECTION_DATA_DEFAULTS,1268            name: 'A',1269            description: 'B',1270            tokenPrefix: 'C',1271            collectionMode: testCase.mode,1272            adminList: [receiver],1273            tokenPropertyPermissions: [1274              {1275                key: 'testKey',1276                permissions: [1277                  {code: TokenPermissionField.Mutable, value: true},1278                  {code: TokenPermissionField.CollectionAdmin, value: true}],1279              },1280              {1281                key: 'testKey_1',1282                permissions: [1283                  {code: TokenPermissionField.Mutable, value: true},1284                  {code: TokenPermissionField.CollectionAdmin, value: true}],1285              },1286            ],1287          },1288        ).send();12891290        const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);1291        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;1292        expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(2);12931294        await collection.methods.deleteProperties(tokenId, ['testKey', 'testKey_1']).send({from: caller});1295        expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(0);1296      }));1297  });12981299  describe('Nesting', () => {1300    itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {1301      const owner = await helper.eth.createAccountWithBalance(donor);1302      const {collectionAddress, collectionId} = await helper.eth.createCollection(1303        owner,1304        {1305          ...CREATE_COLLECTION_DATA_DEFAULTS,1306          name: 'A',1307          description: 'B',1308          tokenPrefix: 'C',1309          collectionMode: 'nft',1310          nestingSettings: {token_owner: true, collection_admin: false, restricted: []},1311        },1312      ).send();13131314      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13151316      // Create a token to be nested to1317      const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});1318      const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;1319      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);13201321      // Create a nested token1322      const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});1323      const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;1324      expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);13251326      // Create a token to be nested and nest1327      const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});1328      const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;13291330      await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});1331      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);13321333      // Unnest token back1334      await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});1335      expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);1336    });13371338    itEth('NFT: collectionNesting()', async ({helper}) => {1339      const owner = await helper.eth.createAccountWithBalance(donor);1340      const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createCollection(1341        owner,1342        new CreateCollectionData('A', 'B', 'C', 'nft'),1343      ).send();13441345      const unnestedContract = await helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);1346      expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);13471348      const {collectionAddress} = await helper.eth.createCollection(1349        owner,1350        {1351          ...CREATE_COLLECTION_DATA_DEFAULTS,1352          name: 'A',1353          description: 'B',1354          tokenPrefix: 'C',1355          collectionMode: 'nft',1356          nestingSettings: {token_owner: true, collection_admin: false, restricted: [unnestedCollectionAddress.toString()]},1357        },1358      ).send();13591360      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);1361      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress.toString()]]);1362      await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});1363      expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);1364    });13651366    itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {1367      const owner = await helper.eth.createAccountWithBalance(donor);13681369      const {collectionId, collectionAddress} = await helper.eth.createCollection(1370        owner,1371        {1372          ...CREATE_COLLECTION_DATA_DEFAULTS,1373          name: 'A',1374          description: 'B',1375          tokenPrefix: 'C',1376          collectionMode: 'nft',1377          nestingSettings: {token_owner: false, collection_admin: false, restricted: []},1378        },1379      ).send();13801381      const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13821383      // Create a token to nest into1384      const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});1385      const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;1386      const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);13871388      // Create a token to nest1389      const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});1390      const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;13911392      // Try to nest1393      await expect(contract.methods1394        .transfer(targetNftTokenAddress, nftTokenId)1395        .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');1396    });1397  });13981399  describe('Flags', () => {1400    const createCollectionData = {1401      ...CREATE_COLLECTION_DATA_DEFAULTS,1402      name: 'A',1403      description: 'B',1404      tokenPrefix: 'C',1405      collectionMode: 'nft' as TCollectionMode,1406    };14071408    itEth('NFT: use numbers for flags', async ({helper}) => {1409      const owner = await helper.eth.createAccountWithBalance(donor);14101411      {1412        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 0}).send();1413        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});1414      }14151416      {1417        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 64}).send();1418        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1419      }1420    });14211422    itEth('NFT: foreign flag number is ignored', async ({helper}) => {1423      const owner = await helper.eth.createAccountWithBalance(donor);14241425      {1426        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).send();1427        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});1428      }14291430      {1431        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).send();1432        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1433      }1434    });14351436    itEth('NFT: use enum for flags', async ({helper}) => {1437      const owner = await helper.eth.createAccountWithBalance(donor);14381439      {1440        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata]}).send();1441        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1442      }1443    });14441445    itEth('NFT: foreign flag enum is ignored', async ({helper}) => {1446      const owner = await helper.eth.createAccountWithBalance(donor);14471448      {1449        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).send();1450        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});1451      }14521453      {1454        const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).send();1455        expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1456      }1457    });1458  });1459});
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
--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -207,7 +207,7 @@
   }
 
   private async createTransaction() {
-    const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(this.signer);
+    const collectionHelper = this.helper.ethNativeContract.collectionHelpers(this.signer);
     let collectionMode;
     switch (this.data.collectionMode) {
       case 'nft': collectionMode = CollectionMode.Nonfungible; break;
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);