git.delta.rocks / unique-network / refs/commits / d4443286d44f

difftreelog

tests(eth): more small tests refactored to playgrounds

Fahrrader2022-09-30parent: #e15be01.patch.diff
in: master

8 files changed

modifiedtests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth
--- a/tests/src/eth/createNFTCollection.test.ts
+++ b/tests/src/eth/createNFTCollection.test.ts
@@ -15,47 +15,49 @@
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
 import {evmToAddress} from '@polkadot/util-crypto';
-import {expect} from 'chai';
-import {getCreatedCollectionCount, getDetailedCollectionInfo} from '../util/helpers';
-import {
-  evmCollectionHelpers,
-  collectionIdToAddress,
-  createEthAccount,
-  createEthAccountWithBalance,
-  evmCollection,
-  itWeb3,
-  getCollectionAddressFromResult,
-} from './util/helpers';
+import {IKeyringPair} from '@polkadot/types/types';
+import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
 
 describe('Create NFT collection from EVM', () => {
-  itWeb3('Create collection', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionHelper = evmCollectionHelpers(web3, owner);
-    const collectionName = 'CollectionEVM';
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = privateKey('//Alice');
+    });
+  });
+
+  itEth('Create collection', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const name = 'CollectionEVM';
     const description = 'Some description';
-    const tokenPrefix = 'token prefix';
-  
-    const collectionCountBefore = await getCreatedCollectionCount(api);
-    const result = await collectionHelper.methods
-      .createNonfungibleCollection(collectionName, description, tokenPrefix)
-      .send();
-    const collectionCountAfter = await getCreatedCollectionCount(api);
-  
-    const {collectionId, collection} = await getCollectionAddressFromResult(api, result);
+    const prefix = 'token prefix';
+
+    // todo:playgrounds this might fail when in async environment.
+    const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+    const {collectionId} = await helper.eth.createNonfungibleCollection(owner, name, description, prefix);
+    const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
+
+    const collection = helper.nft.getCollectionObject(collectionId);
+    const data = (await collection.getData())!;
+    
     expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
     expect(collectionId).to.be.eq(collectionCountAfter);
-    expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName);
-    expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description);
-    expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix);
-    expect(collection.mode.isNft).to.be.true;
+    expect(data.name).to.be.eq(name);
+    expect(data.description).to.be.eq(description);
+    expect(data.raw.tokenPrefix).to.be.eq(prefix);
+    expect(data.raw.mode).to.be.eq('NFT');
   });
 
-  itWeb3('Check collection address exist', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionHelpers = evmCollectionHelpers(web3, owner);
-  
-    const expectedCollectionId = await getCreatedCollectionCount(api) + 1;
-    const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId);
+  // todo:playgrounds this test will fail when in async environment.
+  itEth('Check collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
+    const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
+    const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+
     expect(await collectionHelpers.methods
       .isCollectionExist(expectedCollectionAddress)
       .call()).to.be.false;
@@ -69,31 +71,30 @@
       .call()).to.be.true;
   });
   
-  itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionHelpers = evmCollectionHelpers(web3, owner);
-    let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
-    const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
-    result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
-    let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
-    expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
-    const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
-    expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
-    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
-    const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
+  itEth('Set sponsorship', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const sponsor = await helper.eth.createAccountWithBalance(donor);
+    const ss58Format = helper.chain.getChainProperties().ss58Format;
+    const {collectionId, collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
+
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    await collection.methods.setCollectionSponsor(sponsor).send();
+
+    let data = (await helper.nft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
+
+    await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
+
+    const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
     await sponsorCollection.methods.confirmCollectionSponsorship().send();
-    collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
-    expect(collectionSub.sponsorship.isConfirmed).to.be.true;
-    expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
+
+    data = (await helper.nft.getData(collectionId))!;
+    expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
   });
 
-  itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionHelpers = evmCollectionHelpers(web3, owner);
-    const result = await collectionHelpers.methods.createNonfungibleCollection('Const collection', '5', '5').send();
-    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
+  itEth('Set limits', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionId, collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Limits', 'absolutely anything', 'FLO');
     const limits = {
       accountTokenOwnershipLimit: 1000,
       sponsoredDataSize: 1024,
@@ -106,124 +107,122 @@
       transfersEnabled: false,
     };
 
-    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
-    await collectionEvm.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send();
-    await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send();
-    await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send();
-    await collectionEvm.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send();
-    await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send();
-    await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send();
-    await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send();
-    await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send();
-    await collectionEvm.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send();
+    const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    await collection.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send();
+    await collection.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send();
+    await collection.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send();
+    await collection.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send();
+    await collection.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send();
+    await collection.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send();
+    await collection.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send();
+    await collection.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send();
+    await collection.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send();
     
-    const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
-    expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);
-    expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);
-    expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit);
-    expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);
-    expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);
-    expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);
-    expect(collectionSub.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer);
-    expect(collectionSub.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy);
-    expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled);
+    const data = (await helper.nft.getData(collectionId))!;
+    expect(data.raw.limits.accountTokenOwnershipLimit).to.be.eq(limits.accountTokenOwnershipLimit);
+    expect(data.raw.limits.sponsoredDataSize).to.be.eq(limits.sponsoredDataSize);
+    expect(data.raw.limits.sponsoredDataRateLimit.blocks).to.be.eq(limits.sponsoredDataRateLimit);
+    expect(data.raw.limits.tokenLimit).to.be.eq(limits.tokenLimit);
+    expect(data.raw.limits.sponsorTransferTimeout).to.be.eq(limits.sponsorTransferTimeout);
+    expect(data.raw.limits.sponsorApproveTimeout).to.be.eq(limits.sponsorApproveTimeout);
+    expect(data.raw.limits.ownerCanTransfer).to.be.eq(limits.ownerCanTransfer);
+    expect(data.raw.limits.ownerCanDestroy).to.be.eq(limits.ownerCanDestroy);
+    expect(data.raw.limits.transfersEnabled).to.be.eq(limits.transfersEnabled);
   });
 
-  itWeb3('Collection address exist', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  itEth('Collection address exist', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
     const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
-    const collectionHelpers = evmCollectionHelpers(web3, owner);
-    expect(await collectionHelpers.methods
-      .isCollectionExist(collectionAddressForNonexistentCollection).call())
+    expect(await helper.ethNativeContract.collectionHelpers(collectionAddressForNonexistentCollection)
+      .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
       .to.be.false;
     
-    const result = await collectionHelpers.methods.createNonfungibleCollection('Collection address exist', '7', '7').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    expect(await collectionHelpers.methods
-      .isCollectionExist(collectionIdAddress).call())
+    const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Exister', 'absolutely anything', 'EVC');
+    expect(await helper.ethNativeContract.collectionHelpers(collectionAddress)
+      .methods.isCollectionExist(collectionAddress).call())
       .to.be.true;
   });
 });
 
 describe('(!negative tests!) Create NFT collection from EVM', () => {
-  itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const helper = evmCollectionHelpers(web3, owner);
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = privateKey('//Alice');
+    });
+  });
+
+  itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
     {
-      const MAX_NAME_LENGHT = 64;
-      const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1);
+      const MAX_NAME_LENGTH = 64;
+      const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
       const description = 'A';
       const tokenPrefix = 'A';
-    
-      await expect(helper.methods
+
+      await expect(collectionHelper.methods
         .createNonfungibleCollection(collectionName, description, tokenPrefix)
-        .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGHT);
+        .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
       
     }
-    {  
-      const MAX_DESCRIPTION_LENGHT = 256;
+    {
+      const MAX_DESCRIPTION_LENGTH = 256;
       const collectionName = 'A';
-      const description = 'A'.repeat(MAX_DESCRIPTION_LENGHT + 1);
+      const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
       const tokenPrefix = 'A';
-      await expect(helper.methods
+      await expect(collectionHelper.methods
         .createNonfungibleCollection(collectionName, description, tokenPrefix)
-        .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGHT);
+        .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
     }
-    {  
-      const MAX_TOKEN_PREFIX_LENGHT = 16;
+    {
+      const MAX_TOKEN_PREFIX_LENGTH = 16;
       const collectionName = 'A';
       const description = 'A';
-      const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGHT + 1);
-      await expect(helper.methods
+      const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
+      await expect(collectionHelper.methods
         .createNonfungibleCollection(collectionName, description, tokenPrefix)
-        .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGHT);
+        .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
     }
   });
   
-  itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => {
-    const owner = createEthAccount(web3);
-    const helper = evmCollectionHelpers(web3, owner);
-    const collectionName = 'A';
-    const description = 'A';
-    const tokenPrefix = 'A';
-    
-    await expect(helper.methods
-      .createNonfungibleCollection(collectionName, description, tokenPrefix)
+  itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
+    const owner = helper.eth.createAccount();
+    const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
+    await expect(collectionHelper.methods
+      .createNonfungibleCollection('Peasantry', 'absolutely anything', 'CVE')
       .call()).to.be.rejectedWith('NotSufficientFounds');
   });
 
-  itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const notOwner = createEthAccount(web3);
-    const collectionHelpers = evmCollectionHelpers(web3, owner);
-    const result = await collectionHelpers.methods.createNonfungibleCollection('A', 'A', 'A').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress);
+  itEth('(!negative test!) Check owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const malfeasant = helper.eth.createAccount();
+    const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
+    const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);
     const EXPECTED_ERROR = 'NoPermission';
     {
-      const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-      await expect(contractEvmFromNotOwner.methods
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      await expect(malfeasantCollection.methods
         .setCollectionSponsor(sponsor)
         .call()).to.be.rejectedWith(EXPECTED_ERROR);
       
-      const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
+      const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
       await expect(sponsorCollection.methods
         .confirmCollectionSponsorship()
         .call()).to.be.rejectedWith('caller is not set as sponsor');
     }
     {
-      await expect(contractEvmFromNotOwner.methods
+      await expect(malfeasantCollection.methods
         .setCollectionLimit('account_token_ownership_limit', '1000')
         .call()).to.be.rejectedWith(EXPECTED_ERROR);
     }
   });
 
-  itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const collectionHelpers = evmCollectionHelpers(web3, owner);
-    const result = await collectionHelpers.methods.createNonfungibleCollection('Schema collection', 'A', 'A').send();
-    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
-    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
+  itEth('(!negative test!) Set limits', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'Limits', 'absolutely anything', 'OLF');
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
     await expect(collectionEvm.methods
       .setCollectionLimit('badLimit', 'true')
       .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');
modifiedtests/src/eth/createRFTCollection.test.tsdiffbeforeafterboth
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {evmToAddress} from '@polkadot/util-crypto';17import {evmToAddress} from '@polkadot/util-crypto';
18import {expect} from 'chai';18import {IKeyringPair} from '@polkadot/types/types';
19import {getCreatedCollectionCount, getDetailedCollectionInfo, requirePallets, Pallets} from '../util/helpers';19import {Pallets, requirePalletsOrSkip} from '../util/playgrounds';
20import {20import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
21 evmCollectionHelpers,
22 collectionIdToAddress,
23 createEthAccount,
24 createEthAccountWithBalance,
25 evmCollection,
26 itWeb3,
27 getCollectionAddressFromResult,
28} from './util/helpers';
2921
30describe('Create RFT collection from EVM', () => {22describe('Create RFT collection from EVM', () => {
23 let donor: IKeyringPair;
24
31 before(async function() {25 before(async function() {
32 await requirePallets(this, [Pallets.ReFungible]);26 await usingEthPlaygrounds(async (helper, privateKey) => {
27 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
28 donor = privateKey('//Alice');
29 });
33 });30 });
3431
35 itWeb3('Create collection', async ({api, web3, privateKeyWrapper}) => {32 itEth('Create collection', async ({helper}) => {
36 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);33 const owner = await helper.eth.createAccountWithBalance(donor);
37 const collectionHelper = evmCollectionHelpers(web3, owner);34
38 const collectionName = 'CollectionEVM';35 const name = 'CollectionEVM';
39 const description = 'Some description';36 const description = 'Some description';
40 const tokenPrefix = 'token prefix';37 const prefix = 'token prefix';
41 38
42 const collectionCountBefore = await getCreatedCollectionCount(api);39 // todo:playgrounds this might fail when in async environment.
40 const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
43 const result = await collectionHelper.methods41 const {collectionId} = await helper.eth.createRefungibleCollection(owner, name, description, prefix);
44 .createRFTCollection(collectionName, description, tokenPrefix)
45 .send();
46 const collectionCountAfter = await getCreatedCollectionCount(api);42 const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;
47 43
48 const {collectionId, collection} = await getCollectionAddressFromResult(api, result);44 const data = (await helper.rft.getData(collectionId))!;
45
49 expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);46 expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
50 expect(collectionId).to.be.eq(collectionCountAfter);47 expect(collectionId).to.be.eq(collectionCountAfter);
51 expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName);48 expect(data.name).to.be.eq(name);
52 expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description);49 expect(data.description).to.be.eq(description);
53 expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix);50 expect(data.raw.tokenPrefix).to.be.eq(prefix);
54 expect(collection.mode.isReFungible).to.be.true;51 expect(data.raw.mode).to.be.eq('ReFungible');
55 });52 });
5653
57 itWeb3('Check collection address exist', async ({api, web3, privateKeyWrapper}) => {54 // todo:playgrounds this test will fail when in async environment.
55 itEth('Check collection address exist', async ({helper}) => {
58 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);56 const owner = await helper.eth.createAccountWithBalance(donor);
57
59 const collectionHelpers = evmCollectionHelpers(web3, owner);58 const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
60
61 const expectedCollectionId = await getCreatedCollectionCount(api) + 1;59 const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
62 const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId);60 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
61
63 expect(await collectionHelpers.methods62 expect(await collectionHelpers.methods
64 .isCollectionExist(expectedCollectionAddress)63 .isCollectionExist(expectedCollectionAddress)
73 .call()).to.be.true;72 .call()).to.be.true;
74 });73 });
75 74
76 itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => {75 itEth('Set sponsorship', async ({helper}) => {
77 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);76 const owner = await helper.eth.createAccountWithBalance(donor);
78 const collectionHelpers = evmCollectionHelpers(web3, owner);77 const sponsor = await helper.eth.createAccountWithBalance(donor);
79 let result = await collectionHelpers.methods.createRFTCollection('Sponsor collection', '1', '1').send();78 const ss58Format = helper.chain.getChainProperties().ss58Format;
80 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);79 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
81 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);80
82 const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});81 const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
83 result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();82 await collection.methods.setCollectionSponsor(sponsor).send();
83
84 let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;84 let data = (await helper.rft.getData(collectionId))!;
85 expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
86 const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
87 expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));85 expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
86
88 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');87 await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('caller is not set as sponsor');
88
89 const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);89 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
90 await sponsorCollection.methods.confirmCollectionSponsorship().send();90 await sponsorCollection.methods.confirmCollectionSponsorship().send();
91
91 collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;92 data = (await helper.rft.getData(collectionId))!;
92 expect(collectionSub.sponsorship.isConfirmed).to.be.true;93 expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));
93 expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
94 });94 });
9595
96 itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => {96 itEth('Set limits', async ({helper}) => {
97 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);97 const owner = await helper.eth.createAccountWithBalance(donor);
98 const collectionHelpers = evmCollectionHelpers(web3, owner);98 const {collectionId, collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Limits', 'absolutely anything', 'INSI');
99 const result = await collectionHelpers.methods.createRFTCollection('Const collection', '5', '5').send();
100 const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
101 const limits = {99 const limits = {
102 accountTokenOwnershipLimit: 1000,100 accountTokenOwnershipLimit: 1000,
103 sponsoredDataSize: 1024,101 sponsoredDataSize: 1024,
110 transfersEnabled: false,108 transfersEnabled: false,
111 };109 };
112110
113 const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});111 const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
114 await collectionEvm.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send();112 await collection.methods['setCollectionLimit(string,uint32)']('accountTokenOwnershipLimit', limits.accountTokenOwnershipLimit).send();
115 await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send();113 await collection.methods['setCollectionLimit(string,uint32)']('sponsoredDataSize', limits.sponsoredDataSize).send();
116 await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send();114 await collection.methods['setCollectionLimit(string,uint32)']('sponsoredDataRateLimit', limits.sponsoredDataRateLimit).send();
117 await collectionEvm.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send();115 await collection.methods['setCollectionLimit(string,uint32)']('tokenLimit', limits.tokenLimit).send();
118 await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send();116 await collection.methods['setCollectionLimit(string,uint32)']('sponsorTransferTimeout', limits.sponsorTransferTimeout).send();
119 await collectionEvm.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send();117 await collection.methods['setCollectionLimit(string,uint32)']('sponsorApproveTimeout', limits.sponsorApproveTimeout).send();
120 await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send();118 await collection.methods['setCollectionLimit(string,bool)']('ownerCanTransfer', limits.ownerCanTransfer).send();
121 await collectionEvm.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send();119 await collection.methods['setCollectionLimit(string,bool)']('ownerCanDestroy', limits.ownerCanDestroy).send();
122 await collectionEvm.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send();120 await collection.methods['setCollectionLimit(string,bool)']('transfersEnabled', limits.transfersEnabled).send();
123 121
124 const collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;122 const data = (await helper.rft.getData(collectionId))!;
125 expect(collectionSub.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.be.eq(limits.accountTokenOwnershipLimit);123 expect(data.raw.limits.accountTokenOwnershipLimit).to.be.eq(limits.accountTokenOwnershipLimit);
126 expect(collectionSub.limits.sponsoredDataSize.unwrap().toNumber()).to.be.eq(limits.sponsoredDataSize);124 expect(data.raw.limits.sponsoredDataSize).to.be.eq(limits.sponsoredDataSize);
127 expect(collectionSub.limits.sponsoredDataRateLimit.unwrap().asBlocks.toNumber()).to.be.eq(limits.sponsoredDataRateLimit);125 expect(data.raw.limits.sponsoredDataRateLimit.blocks).to.be.eq(limits.sponsoredDataRateLimit);
128 expect(collectionSub.limits.tokenLimit.unwrap().toNumber()).to.be.eq(limits.tokenLimit);126 expect(data.raw.limits.tokenLimit).to.be.eq(limits.tokenLimit);
129 expect(collectionSub.limits.sponsorTransferTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorTransferTimeout);127 expect(data.raw.limits.sponsorTransferTimeout).to.be.eq(limits.sponsorTransferTimeout);
130 expect(collectionSub.limits.sponsorApproveTimeout.unwrap().toNumber()).to.be.eq(limits.sponsorApproveTimeout);128 expect(data.raw.limits.sponsorApproveTimeout).to.be.eq(limits.sponsorApproveTimeout);
131 expect(collectionSub.limits.ownerCanTransfer.toHuman()).to.be.eq(limits.ownerCanTransfer);129 expect(data.raw.limits.ownerCanTransfer).to.be.eq(limits.ownerCanTransfer);
132 expect(collectionSub.limits.ownerCanDestroy.toHuman()).to.be.eq(limits.ownerCanDestroy);130 expect(data.raw.limits.ownerCanDestroy).to.be.eq(limits.ownerCanDestroy);
133 expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled);131 expect(data.raw.limits.transfersEnabled).to.be.eq(limits.transfersEnabled);
134 });132 });
135133
136 itWeb3('Collection address exist', async ({api, web3, privateKeyWrapper}) => {134 itEth('Collection address exist', async ({helper}) => {
137 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);135 const owner = await helper.eth.createAccountWithBalance(donor);
138 const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';136 const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
139 const collectionHelpers = evmCollectionHelpers(web3, owner);137 expect(await helper.ethNativeContract.collectionHelpers(collectionAddressForNonexistentCollection)
140 expect(await collectionHelpers.methods
141 .isCollectionExist(collectionAddressForNonexistentCollection).call())138 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
142 .to.be.false;139 .to.be.false;
143 140
144 const result = await collectionHelpers.methods.createRFTCollection('Collection address exist', '7', '7').send();141 const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Exister', 'absolutely anything', 'WIWT');
145 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);142 expect(await helper.ethNativeContract.collectionHelpers(collectionAddress)
146 expect(await collectionHelpers.methods
147 .isCollectionExist(collectionIdAddress).call())143 .methods.isCollectionExist(collectionAddress).call())
148 .to.be.true;144 .to.be.true;
149 });145 });
150});146});
151147
152describe('(!negative tests!) Create RFT collection from EVM', () => {148describe('(!negative tests!) Create RFT collection from EVM', () => {
149 let donor: IKeyringPair;
150
153 before(async function() {151 before(async function() {
154 await requirePallets(this, [Pallets.ReFungible]);152 await usingEthPlaygrounds(async (helper, privateKey) => {
153 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);
154 donor = privateKey('//Alice');
155 });
155 });156 });
156157
157 itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3, privateKeyWrapper}) => {158 itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
158 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);159 const owner = await helper.eth.createAccountWithBalance(donor);
159 const helper = evmCollectionHelpers(web3, owner);160 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
160 {161 {
161 const MAX_NAME_LENGHT = 64;162 const MAX_NAME_LENGTH = 64;
162 const collectionName = 'A'.repeat(MAX_NAME_LENGHT + 1);163 const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
163 const description = 'A';164 const description = 'A';
164 const tokenPrefix = 'A';165 const tokenPrefix = 'A';
165 166
166 await expect(helper.methods167 await expect(collectionHelper.methods
167 .createRFTCollection(collectionName, description, tokenPrefix)168 .createRFTCollection(collectionName, description, tokenPrefix)
168 .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGHT);169 .call()).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);
169
170 }170 }
171 { 171 {
172 const MAX_DESCRIPTION_LENGHT = 256;172 const MAX_DESCRIPTION_LENGTH = 256;
173 const collectionName = 'A';173 const collectionName = 'A';
174 const description = 'A'.repeat(MAX_DESCRIPTION_LENGHT + 1);174 const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);
175 const tokenPrefix = 'A';175 const tokenPrefix = 'A';
176 await expect(helper.methods176 await expect(collectionHelper.methods
177 .createRFTCollection(collectionName, description, tokenPrefix)177 .createRFTCollection(collectionName, description, tokenPrefix)
178 .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGHT);178 .call()).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);
179 }179 }
180 { 180 {
181 const MAX_TOKEN_PREFIX_LENGHT = 16;181 const MAX_TOKEN_PREFIX_LENGTH = 16;
182 const collectionName = 'A';182 const collectionName = 'A';
183 const description = 'A';183 const description = 'A';
184 const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGHT + 1);184 const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);
185 await expect(helper.methods185 await expect(collectionHelper.methods
186 .createRFTCollection(collectionName, description, tokenPrefix)186 .createRFTCollection(collectionName, description, tokenPrefix)
187 .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGHT);187 .call()).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);
188 }188 }
189 });189 });
190 190
191 itWeb3('(!negative test!) Create collection (no funds)', async ({web3}) => {191 itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
192 const owner = createEthAccount(web3);192 const owner = helper.eth.createAccount();
193 const helper = evmCollectionHelpers(web3, owner);193 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
194 const collectionName = 'A';194 await expect(collectionHelper.methods
195 const description = 'A';
196 const tokenPrefix = 'A';
197
198 await expect(helper.methods
199 .createRFTCollection(collectionName, description, tokenPrefix)195 .createRFTCollection('Peasantry', 'absolutely anything', 'TWIW')
200 .call()).to.be.rejectedWith('NotSufficientFounds');196 .call()).to.be.rejectedWith('NotSufficientFounds');
201 });197 });
202198
203 itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => {199 itEth('(!negative test!) Check owner', async ({helper}) => {
204 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);200 const owner = await helper.eth.createAccountWithBalance(donor);
205 const notOwner = createEthAccount(web3);201 const peasant = helper.eth.createAccount();
206 const collectionHelpers = evmCollectionHelpers(web3, owner);202 const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
207 const result = await collectionHelpers.methods.createRFTCollection('A', 'A', 'A').send();
208 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);203 const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);
209 const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress, {type: 'ReFungible'});
210 const EXPECTED_ERROR = 'NoPermission';204 const EXPECTED_ERROR = 'NoPermission';
211 {205 {
212 const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);206 const sponsor = await helper.eth.createAccountWithBalance(donor);
213 await expect(contractEvmFromNotOwner.methods207 await expect(peasantCollection.methods
214 .setCollectionSponsor(sponsor)208 .setCollectionSponsor(sponsor)
215 .call()).to.be.rejectedWith(EXPECTED_ERROR);209 .call()).to.be.rejectedWith(EXPECTED_ERROR);
216 210
217 const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);211 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
218 await expect(sponsorCollection.methods212 await expect(sponsorCollection.methods
219 .confirmCollectionSponsorship()213 .confirmCollectionSponsorship()
220 .call()).to.be.rejectedWith('caller is not set as sponsor');214 .call()).to.be.rejectedWith('caller is not set as sponsor');
221 }215 }
222 {216 {
223 await expect(contractEvmFromNotOwner.methods217 await expect(peasantCollection.methods
224 .setCollectionLimit('account_token_ownership_limit', '1000')218 .setCollectionLimit('account_token_ownership_limit', '1000')
225 .call()).to.be.rejectedWith(EXPECTED_ERROR);219 .call()).to.be.rejectedWith(EXPECTED_ERROR);
226 }220 }
227 });221 });
228222
229 itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => {223 itEth('(!negative test!) Set limits', async ({helper}) => {
230 const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);224 const owner = await helper.eth.createAccountWithBalance(donor);
231 const collectionHelpers = evmCollectionHelpers(web3, owner);225 const {collectionAddress} = await helper.eth.createRefungibleCollection(owner, 'Limits', 'absolutely anything', 'ISNI');
232 const result = await collectionHelpers.methods.createRFTCollection('Schema collection', 'A', 'A').send();
233 const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);226 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
234 const collectionEvm = evmCollection(web3, owner, collectionIdAddress, {type: 'ReFungible'});
235 await expect(collectionEvm.methods227 await expect(collectionEvm.methods
236 .setCollectionLimit('badLimit', 'true')228 .setCollectionLimit('badLimit', 'true')
237 .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');229 .call()).to.be.rejectedWith('unknown boolean limit "badLimit"');
modifiedtests/src/eth/crossTransfer.test.tsdiffbeforeafterboth
--- a/tests/src/eth/crossTransfer.test.ts
+++ b/tests/src/eth/crossTransfer.test.ts
@@ -14,7 +14,7 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-import {createCollectionExpectSuccess,
+/*import {createCollectionExpectSuccess,
   createFungibleItemExpectSuccess,
   transferExpectSuccess,
   transferFromExpectSuccess,
@@ -23,81 +23,94 @@
 import {collectionIdToAddress,
   createEthAccountWithBalance,
   subToEth,
-  GAS_ARGS, itWeb3} from './util/helpers';
+  GAS_ARGS, itEth} from './util/helpers';
 import fungibleAbi from './fungibleAbi.json';
-import nonFungibleAbi from './nonFungibleAbi.json';
+import nonFungibleAbi from './nonFungibleAbi.json';*/
+import {itEth, usingEthPlaygrounds} from './util/playgrounds';
+import {CrossAccountId} from '../util/playgrounds/unique';
+import {IKeyringPair} from '@polkadot/types/types';
 
 describe('Token transfer between substrate address and EVM address. Fungible', () => {
-  itWeb3('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
     });
-    const alice = privateKeyWrapper('//Alice');
-    const bob = privateKeyWrapper('//Bob');
-    const charlie = privateKeyWrapper('//Charlie');
-    await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Substrate: alice.address});
-    await transferExpectSuccess(collection, 0, alice, {Ethereum: subToEth(charlie.address)} , 200, 'Fungible');
-    await transferFromExpectSuccess(collection, 0, alice, {Ethereum: subToEth(charlie.address)}, charlie, 50, 'Fungible');
-    await transferExpectSuccess(collection, 0, charlie, bob, 50, 'Fungible');
   });
+  
+  itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => {  
+    const bobCA = CrossAccountId.fromKeyring(bob);
+    const charlieCA = CrossAccountId.fromKeyring(charlie);
 
-  itWeb3('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'Fungible', decimalPoints: 0},
-    });
-    const alice = privateKeyWrapper('//Alice');
-    const bob = privateKeyWrapper('//Bob');
-    await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});
-    const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
 
-    await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, alice.address);
-    await transferExpectSuccess(collection, 0, alice, {Ethereum: aliceProxy} , 200, 'Fungible');
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(fungibleAbi as any, address, {from: aliceProxy, ...GAS_ARGS});
+    await collection.mint(alice, 200n);
+    await collection.transfer(alice, charlieCA.toEthereum(), 200n);
+    await collection.transferFrom(alice, charlieCA.toEthereum(), charlieCA, 50n);
+    await collection.transfer(charlie, bobCA, 50n);
+  });
+
+  itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => {
+    const aliceProxy = await helper.eth.createAccountWithBalance(donor);
+    const bobProxy = await helper.eth.createAccountWithBalance(donor);
 
+    const collection = await helper.ft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(address, 'ft', aliceProxy);
+
+    await collection.mint(alice, 200n, {Ethereum: aliceProxy});
     await contract.methods.transfer(bobProxy, 50).send({from: aliceProxy});
-    await transferFromExpectSuccess(collection, 0, alice, {Ethereum: bobProxy}, bob, 50, 'Fungible');
-    await transferExpectSuccess(collection, 0, bob, alice, 50, 'Fungible');
+    await collection.transferFrom(alice, {Ethereum: bobProxy}, CrossAccountId.fromKeyring(bob), 50n);
+    await collection.transfer(bob, CrossAccountId.fromKeyring(alice), 50n);
   });
 });
 
 describe('Token transfer between substrate address and EVM address. NFT', () => {
-  itWeb3('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'NFT'},
+  let donor: IKeyringPair;
+  let alice: IKeyringPair;
+  let bob: IKeyringPair;
+  let charlie: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (helper, privateKey) => {
+      donor = privateKey('//Alice');
+      [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
     });
-    const alice = privateKeyWrapper('//Alice');
-    const bob = privateKeyWrapper('//Bob');
-    const charlie = privateKeyWrapper('//Charlie');
-    await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
-    await transferExpectSuccess(collection, tokenId, alice, {Ethereum: subToEth(charlie.address)}, 1, 'NFT');
-    await transferFromExpectSuccess(collection, tokenId, alice, {Ethereum: subToEth(charlie.address)}, charlie, 1, 'NFT');
-    await transferExpectSuccess(collection, tokenId, charlie, bob, 1, 'NFT');
   });
+  
+  itEth('The private key X create a substrate address. Alice sends a token to the corresponding EVM address, and X can send it to Bob in the substrate', async ({helper}) => {
+    const charlieEth = CrossAccountId.fromKeyring(charlie, 'Ethereum');
+    
+    const collection = await helper.nft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+    const token = await collection.mintToken(alice);
+    await token.transfer(alice, charlieEth);
+    await token.transferFrom(alice, charlieEth, CrossAccountId.fromKeyring(charlie));
+    await token.transfer(charlie, CrossAccountId.fromKeyring(bob));
+  });
 
-  itWeb3('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({api, web3, privateKeyWrapper}) => {
-    const collection = await createCollectionExpectSuccess({
-      name: 'token name',
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
-    const bob = privateKeyWrapper('//Bob');
-    const charlie = privateKeyWrapper('//Charlie');
-    await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});
-    const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
-    await transferExpectSuccess(collection, tokenId, alice, {Ethereum: aliceProxy} , 1, 'NFT');
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: aliceProxy, ...GAS_ARGS});
+  itEth('The private key X create a EVM address. Alice sends a token to the substrate address corresponding to this EVM address, and X can send it to Bob in the EVM', async ({helper}) => {
+    const aliceProxy = await helper.eth.createAccountWithBalance(donor);
+    const bobProxy = await helper.eth.createAccountWithBalance(donor);
+
+    const collection = await helper.nft.mintCollection(alice);
+    await collection.setLimits(alice, {ownerCanTransfer: true});
+
+    const address = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(address, 'nft', aliceProxy);
+
+    const token = await collection.mintToken(alice);
+    await token.transfer(alice, {Ethereum: aliceProxy});
     await contract.methods.transfer(bobProxy, 1).send({from: aliceProxy});
-    await transferFromExpectSuccess(collection, tokenId, alice, {Ethereum: bobProxy}, bob, 1, 'NFT');
-    await transferExpectSuccess(collection, tokenId, bob, charlie, 1, 'NFT');
+    await token.transferFrom(alice, {Ethereum: bobProxy}, {Substrate: bob.address});
+    await token.transfer(bob, {Substrate: charlie.address});
   });
 });
modifiedtests/src/eth/helpersSmoke.test.tsdiffbeforeafterboth
--- a/tests/src/eth/helpersSmoke.test.ts
+++ b/tests/src/eth/helpersSmoke.test.ts
@@ -14,21 +14,30 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-import {expect} from 'chai';
-import {createEthAccountWithBalance, deployFlipper, itWeb3, contractHelpers} from './util/helpers';
+import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
 
 describe('Helpers sanity check', () => {
-  itWeb3('Contract owner is recorded', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+  let donor: IKeyringPair;
 
-    const flipper = await deployFlipper(web3, owner);
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = privateKey('//Alice');
+    });
+  });
+  
+  itEth('Contract owner is recorded', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
 
-    expect(await contractHelpers(web3, owner).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner);
+    const flipper = await helper.eth.deployFlipper(owner);
+
+    expect(await helper.ethNativeContract.contractHelpers(owner).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner);
   });
 
-  itWeb3('Flipper is working', async ({api, web3, privateKeyWrapper}) => {
-    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const flipper = await deployFlipper(web3, owner);
+  itEth('Flipper is working', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+
+    const flipper = await helper.eth.deployFlipper(owner);
 
     expect(await flipper.methods.getValue().call()).to.be.false;
     await flipper.methods.flip().send({from: owner});
modifiedtests/src/eth/migration.test.tsdiffbeforeafterboth
--- a/tests/src/eth/migration.test.ts
+++ b/tests/src/eth/migration.test.ts
@@ -14,12 +14,20 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-import {expect} from 'chai';
-import {submitTransactionAsync} from '../substrate/substrate-api';
-import {createEthAccountWithBalance, GAS_ARGS, itWeb3} from './util/helpers';
+import {expect, itEth, usingEthPlaygrounds} from './util/playgrounds';
+import {IKeyringPair} from '@polkadot/types/types';
 
 describe('EVM Migrations', () => {
-  itWeb3('Deploy contract saved state', async ({web3, api, privateKeyWrapper}) => {
+  let superuser: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      superuser = privateKey('//Alice');
+    });
+  });
+  
+  // todo:playgrounds requires sudo, look into later
+  itEth('Deploy contract saved state', async ({helper}) => {
     /*
       contract StatefulContract {
         uint counter;
@@ -53,13 +61,16 @@
       ['0xedc95719e9a3b28dd8e80877cb5880a9be7de1a13fc8b05e7999683b6b567643', '0x0000000000000000000000000000000000000000000000000000000000000004'],
     ];
 
-    const alice = privateKeyWrapper('//Alice');
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+    const caller = await helper.eth.createAccountWithBalance(superuser);
 
-    await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.begin(ADDRESS) as any));
-    await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.setData(ADDRESS, DATA as any) as any));
-    await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.finish(ADDRESS, CODE) as any));
+    const txBegin = helper.constructApiCall('api.tx.evmMigration.begin', [ADDRESS]);
+    const txSetData = helper.constructApiCall('api.tx.evmMigration.setData', [ADDRESS, DATA]);
+    const txFinish = helper.constructApiCall('api.tx.evmMigration.finish', [ADDRESS, CODE]);
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txBegin])).to.be.fulfilled;
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txSetData])).to.be.fulfilled;
+    await expect(helper.executeExtrinsic(superuser, 'api.tx.sudo.sudo', [txFinish])).to.be.fulfilled;
 
+    const web3 = helper.getWeb3();
     const contract = new web3.eth.Contract([
       {
         inputs: [],
@@ -87,7 +98,7 @@
         stateMutability: 'view',
         type: 'function',
       },
-    ], ADDRESS, {from: caller, ...GAS_ARGS});
+    ], ADDRESS, {from: caller, gas: helper.eth.DEFAULT_GAS});
 
     expect(await contract.methods.counterValue().call()).to.be.equal('10');
     for (let i = 1; i <= 4; i++) {
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -171,70 +171,38 @@
   });
 
   //TODO: CORE-302 add eth methods
-  /* todo:playgrounds skipped test!
-  itWeb3.skip('Can perform mintBulk()', async ({helper}) => {
-    const collection = await createCollectionExpectSuccess({
-      mode: {type: 'NFT'},
-    });
-    const alice = privateKeyWrapper('//Alice');
+  itEth.skip('Can perform mintBulk()', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const receiver = helper.eth.createAccount();
 
-    const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
-    const changeAdminTx = api.tx.unique.addCollectionAdmin(collection, {Ethereum: caller});
-    await submitTransactionAsync(alice, changeAdminTx);
-    const receiver = createEthAccount(web3);
+    const collection = await helper.nft.mintCollection(alice);
+    await collection.addAdmin(alice, {Ethereum: caller});
 
-    const address = collectionIdToAddress(collection);
-    const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
-
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', caller);
     {
+      const bulkSize = 3;
       const nextTokenId = await contract.methods.nextTokenId().call();
       expect(nextTokenId).to.be.equal('1');
       const result = await contract.methods.mintBulkWithTokenURI(
         receiver,
-        [
-          [nextTokenId, 'Test URI 0'],
-          [+nextTokenId + 1, 'Test URI 1'],
-          [+nextTokenId + 2, 'Test URI 2'],
-        ],
+        Array.from({length: bulkSize}, (_, i) => (
+          [+nextTokenId + i, `Test URI ${i}`]
+        )),
       ).send({from: caller});
-      const events = normalizeEvents(result.events);
 
-      expect(events).to.be.deep.equal([
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: nextTokenId,
-          },
-        },
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: String(+nextTokenId + 1),
-          },
-        },
-        {
-          address,
-          event: 'Transfer',
-          args: {
-            from: '0x0000000000000000000000000000000000000000',
-            to: receiver,
-            tokenId: String(+nextTokenId + 2),
-          },
-        },
-      ]);
+      const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
+      for (let i = 0; i < bulkSize; i++) {
+        const event = events[i];
+        expect(event.address).to.equal(collectionAddress);
+        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+        expect(event.returnValues.to).to.equal(receiver);
+        expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);
 
-      expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI 0');
-      expect(await contract.methods.tokenURI(+nextTokenId + 1).call()).to.be.equal('Test URI 1');
-      expect(await contract.methods.tokenURI(+nextTokenId + 2).call()).to.be.equal('Test URI 2');
+        expect(await contract.methods.tokenURI(+nextTokenId + i).call()).to.be.equal(`Test URI ${i}`);
+      }
     }
   });
-  */
 
   itEth('Can perform burn()', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
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
@@ -131,7 +131,7 @@
   }
 }
 
-  
+
 class EthGroup extends EthGroupBase {
   DEFAULT_GAS = 2_500_000;
 
@@ -164,14 +164,14 @@
       true,
     );
   }
-  
+
   async callEVM(signer: TEthereumAccount, contractAddress: string, abi: string) {
     return await this.helper.callRpc('api.rpc.eth.call', [{from: signer, to: contractAddress, data: abi}]);
   }
 
   async createNonfungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
     const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
-        
+
     const result = await collectionHelper.methods.createNonfungibleCollection(name, description, tokenPrefix).send();
 
     const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
@@ -182,7 +182,7 @@
 
   async createRefungibleCollection(signer: string, name: string, description: string, tokenPrefix: string): Promise<{collectionId: number, collectionAddress: string}> {
     const collectionHelper = this.helper.ethNativeContract.collectionHelpers(signer);
-        
+
     const result = await collectionHelper.methods.createRFTCollection(name, description, tokenPrefix).send();
 
     const collectionAddress = this.helper.ethAddress.normalizeAddress(result.events.CollectionCreated.returnValues.collectionId);
@@ -246,7 +246,7 @@
     return before - after;
   }
 }  
-  
+
 class EthAddressGroup extends EthGroupBase {
   extractCollectionId(address: string): number {
     if (!(address.length === 42 || address.length === 40)) throw new Error('address wrong format');
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -20,8 +20,11 @@
     if (account.Ethereum) this.Ethereum = account.Ethereum;
   }
 
-  static fromKeyring(account: IKeyringPair) {
-    return new CrossAccountId({Substrate: account.address});
+  static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {
+    switch (domain) {
+      case 'Substrate': return new CrossAccountId({Substrate: account.address});
+      case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();
+    }
   }
 
   static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {
@@ -40,6 +43,24 @@
     if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);
     return this;
   }
+
+  static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {
+    return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));
+  }
+
+  toEthereum(): CrossAccountId {
+    if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});
+    return this;
+  }
+
+  static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {
+    return evmToAddress(address, ss58Format);
+  }
+
+  toSubstrate(ss58Format?: number): CrossAccountId {
+    if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});
+    return this;
+  }
   
   toLowerCase(): CrossAccountId {
     if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();
@@ -2093,9 +2114,8 @@
    * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network
    * @returns address in chain format
    */
-  async normalizeSubstrateToChainFormat(address: TSubstrateAccount): Promise<TSubstrateAccount> {
-    const info = this.helper.chain.getChainProperties();
-    return encodeAddress(decodeAddress(address), info.ss58Format);
+  normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {
+    return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);
   }
 
   /**
@@ -2105,10 +2125,8 @@
    * @example ethToSubstrate('0x9F0583DbB855d...')
    * @returns substrate mirror of a provided ethereum address
    */
-  async ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): Promise<TSubstrateAccount> {
-    if(!toChainFormat) return evmToAddress(ethAddress);
-    const info = this.helper.chain.getChainProperties();
-    return evmToAddress(ethAddress, info.ss58Format);
+  ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {
+    return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);
   }
 
   /**
@@ -2118,7 +2136,7 @@
    * @returns ethereum mirror of a provided substrate address
    */
   substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {
-    return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(subAddress), i => i.toString(16).padStart(2, '0')).join(''));
+    return CrossAccountId.translateSubToEth(subAddress);
   }
 }