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

difftreelog

chore fix code review requests

Grigoriy Simonov2022-12-23parent: #39a2c66.patch.diff
in: master

3 files changed

modifiedruntime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth
--- a/runtime/common/ethereum/sponsoring/refungible.rs
+++ b/runtime/common/ethereum/sponsoring/refungible.rs
@@ -233,15 +233,22 @@
 			| TokenContractAddress { .. } => None,
 
 			// Not sponsored
-			TransferCross { .. }
-			| TransferFromCross { .. }
 			| BurnFrom { .. }
-			| BurnFromCross { .. }
-			| MintCross { .. }
+			| BurnFromCross { .. } => None,
+			
+			MintCross { .. }
 			| MintBulk { .. }
-			| MintBulkWithTokenUri { .. } => None,
+			| MintBulkWithTokenUri { .. } => {
+				withdraw_create_item::<T>(
+					&collection,
+					&who,
+					&CreateItemData::NFT(CreateNftData::default()),
+				)
+			}
 
-			Transfer { token_id, .. } => {
+			TransferCross { token_id, .. }
+			| TransferFromCross { token_id, .. }
+			| Transfer { token_id, .. } => {
 				let token_id = TokenId::try_from(token_id).ok()?;
 				withdraw_transfer::<T>(&collection, &who, &token_id)
 			}
@@ -321,8 +328,8 @@
 
 	pub fn unique_extensions_call_sponsor<T>(
 		call: ERC20UniqueExtensionsCall<T>,
-		_token: RefungibleTokenHandle<T>,
-		_who: &T::CrossAccountId,
+		token: RefungibleTokenHandle<T>,
+		who: &T::CrossAccountId,
 	) -> Option<()>
 	where
 		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,
@@ -336,10 +343,20 @@
 			// Not sponsored
 			BurnFrom { .. }
 			| BurnFromCross { .. }
-			| ApproveCross { .. }
-			| TransferCross { .. }
-			| TransferFromCross { .. }
 			| Repartition { .. } => None,
+			
+			TransferCross { .. }
+			| TransferFromCross { .. } => {
+				let RefungibleTokenHandle(handle, token_id) = token;
+				let token_id = token_id.try_into().ok()?;
+				withdraw_transfer::<T>(&handle, &who, &token_id)
+			}
+
+			ApproveCross { .. } => {
+				let RefungibleTokenHandle(handle, token_id) = token;
+				let token_id = token_id.try_into().ok()?;
+				withdraw_approve::<T>(&handle, who.as_sub(), &token_id)
+			}
 		}
 	}
 }
modifiedtests/src/eth/abi/reFungibleDeprecated.jsondiffbeforeafterboth
before · tests/src/eth/abi/reFungibleDeprecated.json
1[2  {3    "inputs": [4      { "internalType": "address", "name": "newAdmin", "type": "address" }5    ],6    "name": "addCollectionAdmin",7    "outputs": [],8    "stateMutability": "nonpayable",9    "type": "function"10  },11  {12    "inputs": [13      { "internalType": "address", "name": "user", "type": "address" }14    ],15    "name": "addToCollectionAllowList",16    "outputs": [],17    "stateMutability": "nonpayable",18    "type": "function"19  },20  {21    "inputs": [22      { "internalType": "address", "name": "from", "type": "address" },23      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }24    ],25    "name": "burnFrom",26    "outputs": [],27    "stateMutability": "nonpayable",28    "type": "function"29  },30  {31    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],32    "name": "deleteCollectionProperty",33    "outputs": [],34    "stateMutability": "nonpayable",35    "type": "function"36  },37  {38    "inputs": [39      { "internalType": "address", "name": "user", "type": "address" }40    ],41    "name": "isOwnerOrAdmin",42    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],43    "stateMutability": "view",44    "type": "function"45  },46  {47    "inputs": [48      { "internalType": "address", "name": "admin", "type": "address" }49    ],50    "name": "removeCollectionAdmin",51    "outputs": [],52    "stateMutability": "nonpayable",53    "type": "function"54  },55  {56    "inputs": [57      { "internalType": "address", "name": "user", "type": "address" }58    ],59    "name": "removeFromCollectionAllowList",60    "outputs": [],61    "stateMutability": "nonpayable",62    "type": "function"63  },64  {65    "inputs": [66      { "internalType": "string", "name": "key", "type": "string" },67      { "internalType": "bytes", "name": "value", "type": "bytes" }68    ],69    "name": "setCollectionProperty",70    "outputs": [],71    "stateMutability": "nonpayable",72    "type": "function"73  },74  {75    "inputs": [76      { "internalType": "address", "name": "sponsor", "type": "address" }77    ],78    "name": "setCollectionSponsor",79    "outputs": [],80    "stateMutability": "nonpayable",81    "type": "function"82  },83  {84    "inputs": [85      { "internalType": "address", "name": "newOwner", "type": "address" }86    ],87    "name": "changeCollectionOwner",88    "outputs": [],89    "stateMutability": "nonpayable",90    "type": "function"91  },92  {93    "inputs": [94      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },95      { "internalType": "string", "name": "key", "type": "string" }96    ],97    "name": "deleteProperty",98    "outputs": [],99    "stateMutability": "nonpayable",100    "type": "function"101  }102]
modifiedtests/src/eth/collectionSponsoring.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collectionSponsoring.test.ts
+++ b/tests/src/eth/collectionSponsoring.test.ts
@@ -16,6 +16,7 @@
 
 import {IKeyringPair} from '@polkadot/types/types';
 import {Pallets, requirePalletsOrSkip, usingPlaygrounds} from '../util/index';
+import { CrossAccountId } from '../util/playgrounds/unique';
 import {itEth, expect} from './util';
 
 describe('evm nft collection sponsoring', () => {
@@ -158,6 +159,7 @@
 
       const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
       const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
 
       // User can mint token without balance:
       {
@@ -181,7 +183,7 @@
 
         expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
         expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-        expect(userBalanceAfter).to.be.eq(0n);
+        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
         expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
       }
     }));
@@ -332,32 +334,67 @@
     });
   });
   
-  itEth('sponsors mint transactions', async ({helper}) => {
-    const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}});
-    await collection.setSponsor(alice, alice.address);
-    await collection.confirmSponsorship(alice);
+  [
+    'mintCross',
+    'mintWithTokenURI',
+    'mintBulk',
+    'mintBulkWithTokenUri',
+  ].map(testCase => 
+    itEth(`[${testCase}] sponsors mint transactions`, async ({helper}) => {
+      const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'spnr', permissions: {mintMode: true}, tokenPropertyPermissions: [
+        {key: 'URI', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
+      ]});
+      
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      await collection.setSponsor(alice, alice.address);
+      await collection.confirmSponsorship(alice);
 
-    const minter = helper.eth.createAccount();
-    expect(await helper.balance.getEthereum(minter)).to.equal(0n);
+      const minter = helper.eth.createAccount();
+      const minterCross = helper.ethCrossAccount.fromAddress(minter);
+      expect(await helper.balance.getEthereum(minter)).to.equal(0n);
 
-    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', minter);
+      const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+      const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', minter, true);
 
-    await collection.addToAllowList(alice, {Ethereum: minter});
+      await collection.addToAllowList(alice, {Ethereum: minter});
+      helper.collection.addAdmin(alice, collection.collectionId, {Ethereum: owner});
+      const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
+      await collectionHelpers.methods.makeCollectionERC721MetadataCompatible(collectionAddress, 'base/')
+        .send();
 
-    const result = await contract.methods.mint(minter).send();
+      let mintingResult;
+      let tokenId;
+      const nextTokenId = await contract.methods.nextTokenId().call();
+      switch (testCase) {
+        case 'mintCross':
+          mintingResult = await contract.methods.mintCross(minterCross, []).send();
+          break;
+        case 'mintWithTokenURI':
+          mintingResult = await contract.methods.mintWithTokenURI(minter, 'Test URI').send();
+          tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+          expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+          break;
+        case 'mintBulk':
+          mintingResult = await contract.methods.mintBulk(minter, [nextTokenId]).send();
+          break;
+        case 'mintBulkWithTokenUri':
+          mintingResult = await contract.methods.mintBulkWithTokenURI(minter, [[nextTokenId, 'Test URI']]).send();
+          tokenId = mintingResult.events.Transfer.returnValues.tokenId;
+          expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
+          break;
+      }
 
-    const events = helper.eth.normalizeEvents(result.events);
-    expect(events).to.deep.include({
-      address: collectionAddress,
-      event: 'Transfer',
-      args: {
-        from: '0x0000000000000000000000000000000000000000',
-        to: minter,
-        tokenId: '1',
-      },
-    });
-  });
+      const events = helper.eth.normalizeEvents(mintingResult.events);
+      expect(events).to.deep.include({
+        address: collectionAddress,
+        event: 'Transfer',
+        args: {
+          from: '0x0000000000000000000000000000000000000000',
+          to: minter,
+          tokenId: '1',
+        },
+      });
+    }));
 
   [
     'setCollectionSponsorCross',
@@ -405,7 +442,6 @@
       let sponsorship = (await collectionSub.getData())!.raw.sponsorship;
       expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));
       // Account cannot confirm sponsorship if it is not set as a sponsor
-      //await collectionEvm.methods.confirmCollectionSponsorship().call();
       await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
       
       // Sponsor can confirm sponsorship:
@@ -434,6 +470,7 @@
   
       const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
       const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));
+      const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
 
       // User can mint token without balance:
       {
@@ -456,7 +493,7 @@
   
         expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
         expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
-        expect(userBalanceAfter).to.be.eq(0n);
+        expect(userBalanceAfter).to.be.eq(userBalanceBefore);
         expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
       }
     }));
@@ -465,7 +502,7 @@
     'setCollectionSponsorCross',
     'setCollectionSponsor', // Soft-deprecated
   ].map(testCase => 
-    itEth(`[${testCase}] Check that transaction via EVM spend money from sponsor address`, async ({helper}) => {
+    itEth(`[${testCase}] Check that collection admin EVM transaction spend money from sponsor eth address`, async ({helper}) => {
       const owner = await helper.eth.createAccountWithBalance(donor);
       const sponsor = await helper.eth.createAccountWithBalance(donor);
       const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
@@ -475,15 +512,21 @@
       const collectionSub = helper.rft.getCollectionObject(collectionId);
       const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, testCase === 'setCollectionSponsor');
       // Set collection sponsor:
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
       await collectionEvm.methods[testCase](testCase === 'setCollectionSponsor' ? sponsor : sponsorCross).send();
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
       let collectionData = (await collectionSub.getData())!;
       expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
       await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
   
       await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
       collectionData = (await collectionSub.getData())!;
       expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));
-  
+      expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
+      const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
+      expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorTuple.sub))).to.be.equal(helper.address.ethToSubstrate(sponsor));
+
       const user = helper.eth.createAccount();
       const userCross = helper.ethCrossAccount.fromAddress(user);
       await collectionEvm.methods.addCollectionAdminCross(userCross).send();
@@ -514,7 +557,7 @@
       expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
     }));
 
-  itEth('Check that transaction via EVM spend money from substrate sponsor address', async ({helper}) => {
+  itEth('Check that collection admin EVM transaction spend money from sponsor sub address', async ({helper}) => {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const sponsor = alice;
     const sponsorCross = helper.ethCrossAccount.fromKeyringPair(sponsor);
@@ -527,9 +570,16 @@
     expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
     await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
     expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.true;
+    let collectionData = (await collectionSub.getData())!;
+    expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(sponsor.address);
+    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
 
     await collectionSub.confirmSponsorship(sponsor);
+    collectionData = (await collectionSub.getData())!;
+    expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(sponsor.address);
     expect(await collectionEvm.methods.hasCollectionPendingSponsor().call()).to.be.false;
+    const sponsorTuple = await collectionEvm.methods.collectionSponsor().call({from: owner});
+    expect(BigInt(sponsorTuple.sub)).to.be.equal(BigInt('0x' + Buffer.from(sponsor.addressRaw).toString('hex')));
 
     const user = helper.eth.createAccount();
     const userCross = helper.ethCrossAccount.fromAddress(user);
@@ -585,6 +635,7 @@
 
     const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
     const sponsorBalanceBefore = await helper.balance.getSubstrate(sponsor.address);
+    const userBalanceBefore =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
 
     {
       const nextTokenId = await collectionEvm.methods.nextTokenId().call();
@@ -609,9 +660,11 @@
 
       const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
       const sponsorBalanceAfter = await helper.balance.getSubstrate(sponsor.address);
+      const userBalanceAfter =  await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
 
       expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
       expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
       expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;
     }
   });
@@ -636,6 +689,58 @@
     const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;
     expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});
   });
+
+  [
+    'transfer',
+    'transferCross',
+    'transferFrom',
+    'transferFromCross',
+  ].map(testCase => 
+    itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+
+      const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      const receiver = await helper.eth.createAccountWithBalance(donor);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+
+      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+
+      const user = await helper.eth.createAccountWithBalance(donor);
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+
+      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = result.events.Transfer.returnValues.tokenId;
+      
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+
+      switch (testCase) {
+        case 'transfer':
+          await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});
+          break;
+        case 'transferCross':
+          await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
+          break;
+        case 'transferFrom':
+          await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});
+          break;
+        case 'transferFromCross':
+          await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});
+          break;
+      }
+
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+    }));
 });
 
 describe('evm RFT token sponsoring', () => {
@@ -648,117 +753,106 @@
     });
   });
 
-  itEth('[cross] Check that transfer via EVM spend money from sponsor address', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
+  [
+    'transfer',
+    'transferCross',
+    'transferFrom',
+    'transferFromCross',
+  ].map(testCase => 
+    itEth(`[${testCase}] Check that token piece transfer via EVM spend money from sponsor address`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
 
-    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      const receiver = await helper.eth.createAccountWithBalance(donor);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
 
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
-    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
 
-    const user = await helper.eth.createAccountWithBalance(donor);
-    const userCross = helper.ethCrossAccount.fromAddress(user);
-    await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+      const user = await helper.eth.createAccountWithBalance(donor);
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
 
-    const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-    const tokenId = result.events.Transfer.returnValues.tokenId;
+      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = result.events.Transfer.returnValues.tokenId;
 
-    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);    
-    await tokenContract.methods.repartition(2).send();
-    
-    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+      const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);   
+      await tokenContract.methods.repartition(2).send();
+      
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
 
-    await tokenContract.methods.transfer(receiver, 1).send();
+      switch (testCase) {
+        case 'transfer':
+          await tokenContract.methods.transfer(receiver, 1).send();
+          break;
+        case 'transferCross':
+          await tokenContract.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();
+          break;
+        case 'transferFrom':
+          await tokenContract.methods.transferFrom(user, receiver, 1).send();
+          break;
+        case 'transferFromCross':
+          await tokenContract.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), 1).send();
+          break;
+      }
 
-    const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-    const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-    const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-    expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-  });
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+    }));
 
-  itEth('[cross] Check that approve via EVM spend money from sponsor address', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
+  [
+    'approve',
+    'approveCross',
+  ].map(testCase => 
+    itEth(`[${testCase}] Check that approve via EVM spend money from sponsor address`, async ({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
 
-    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+      const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
+      const sponsor = await helper.eth.createAccountWithBalance(donor);
+      const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
+      const receiver = await helper.eth.createAccountWithBalance(donor);
+      const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
 
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
-    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+      await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
+      await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
 
-    const user = await helper.eth.createAccountWithBalance(donor);
-    const userCross = helper.ethCrossAccount.fromAddress(user);
-    await collectionEvm.methods.addCollectionAdminCross(userCross).send();
+      const user = await helper.eth.createAccountWithBalance(donor);
+      const userCross = helper.ethCrossAccount.fromAddress(user);
+      await collectionEvm.methods.addCollectionAdminCross(userCross).send();
 
-    const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-    const tokenId = result.events.Transfer.returnValues.tokenId;
+      const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
+      const tokenId = result.events.Transfer.returnValues.tokenId;
 
-    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);    
-    await tokenContract.methods.repartition(2).send();
-    
-    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+      const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);  
+      await tokenContract.methods.repartition(2).send();
+      
+      const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
 
-    await tokenContract.methods.approve(receiver, 1).send();
+      switch (testCase) {
+        case 'approve':
+          await tokenContract.methods.approve(receiver, 1).send();
+          break;
+        case 'approveCross':
+          await tokenContract.methods.approveCross(helper.ethCrossAccount.fromAddress(receiver), 1).send();
+          break;
+      }
 
-    const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-    const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-    const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-    expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-  });
-  
-
-  itEth('[cross] Check that transferFrom via EVM spend money from sponsor address', async ({helper}) => {
-    const owner = await helper.eth.createAccountWithBalance(donor);
-
-    const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner,'Sponsor collection', '1', '1', '');
-    const sponsor = await helper.eth.createAccountWithBalance(donor);
-    const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
-    const receiver = await helper.eth.createAccountWithBalance(donor);
-    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+      const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
+      expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
+      const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
+      expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
+      expect(userBalanceAfter).to.be.eq(userBalanceBefore);
+    }));
+});
 
-    await collectionEvm.methods.setCollectionSponsorCross(sponsorCross).send();
-    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
-
-    const user = await helper.eth.createAccountWithBalance(donor);
-    const userCross = helper.ethCrossAccount.fromAddress(user);
-    await collectionEvm.methods.addCollectionAdminCross(userCross).send();
-
-    const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});
-    const tokenId = result.events.Transfer.returnValues.tokenId;
-
-    const tokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, user);    
-    await tokenContract.methods.repartition(2).send();
-    await tokenContract.methods.approve(receiver, 1).send();
-    
-    const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-    const receiverBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(receiver));
-
-    const receiverTokenContract = helper.ethNativeContract.rftTokenById(collectionId, tokenId, receiver);   
-    await receiverTokenContract.methods.transferFrom(user, receiver, 1).send();
-
-    const receiverBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(receiver));
-    expect(receiverBalanceAfter).to.be.eq(receiverBalanceBefore);
-    const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));
-    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-    const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));
-    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
-    const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));
-    expect(userBalanceAfter).to.be.eq(userBalanceBefore);
-  });
-});