git.delta.rocks / unique-network / refs/commits / 752e2b099754

difftreelog

fix update weighting for mintBulkCross and forbid multiple owners + multipe tokens

Grigoriy Simonov2023-09-22parent: #0c121c7.patch.diff
in: master

2 files changed

modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
10411041
1042 /// @notice Function to mint a token.1042 /// @notice Function to mint a token.
1043 /// @param tokenProperties Properties of minted token1043 /// @param tokenProperties Properties of minted token
1044 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_properties.len() as u32) + <SelfWeightOf<T>>::set_token_properties(token_properties.len() as u32))]1044 #[weight(if token_properties.len() == 1 {
1045 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(token_properties.iter().next().unwrap().owners.len() as u32)
1046 } else {
1047 <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(token_properties.len() as u32)
1048 } + <SelfWeightOf<T>>::set_token_properties(token_properties.len() as u32))]
1045 fn mint_bulk_cross(1049 fn mint_bulk_cross(
1046 &mut self,1050 &mut self,
1047 caller: Caller,1051 caller: Caller,
1051 let budget = self1055 let budget = self
1052 .recorder1056 .recorder
1053 .weight_calls_budget(<StructureWeight<T>>::find_parent());1057 .weight_calls_budget(<StructureWeight<T>>::find_parent());
1058 let has_multiple_tokens = token_properties.len() > 1;
10541059
1055 let mut create_rft_data = Vec::with_capacity(token_properties.len());1060 let mut create_rft_data = Vec::with_capacity(token_properties.len());
1056 for MintTokenData { owners, properties } in token_properties {1061 for MintTokenData { owners, properties } in token_properties {
1062 let has_multiple_owners = owners.len() > 1;
1063 if has_multiple_tokens & has_multiple_owners {
1064 return Err(
1065 "creation of multiple tokens supported only if they have single owner each"
1066 .into(),
1067 );
1068 }
1057 let users: BoundedBTreeMap<_, _, _> = owners1069 let users: BoundedBTreeMap<_, _, _> = owners
1058 .into_iter()1070 .into_iter()
1059 .map(|data| Ok((data.owner.into_sub_cross_account::<T>()?, data.pieces)))1071 .map(|data| Ok((data.owner.into_sub_cross_account::<T>()?, data.pieces)))
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -126,13 +126,11 @@
     }
   });
 
-  itEth('Can perform mintBulkCross()', async ({helper}) => {
+  itEth('Can perform mintBulkCross() with multiple tokens', async ({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
     const callerCross = helper.ethCrossAccount.fromAddress(caller);
     const receiver = helper.eth.createAccount();
     const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
-    const receiver2 = helper.eth.createAccount();
-    const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);
 
     const permissions = [
       {code: TokenPermissionField.Mutable, value: true},
@@ -160,81 +158,135 @@
     ).send();
 
     const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
-    {
-      const nextTokenId = await contract.methods.nextTokenId().call();
-      expect(nextTokenId).to.be.equal('1');
-      const result = await contract.methods.mintBulkCross([
+    const nextTokenId = await contract.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+    const result = await contract.methods.mintBulkCross([
+      {
+        owners: [{
+          owner: receiverCross,
+          pieces: 1,
+        }],
+        properties: [
+          {key: 'key_0_0', value: Buffer.from('value_0_0')},
+        ],
+      },
+      {
+        owners: [{
+          owner: receiverCross,
+          pieces: 2,
+        }],
+        properties: [
+          {key: 'key_1_0', value: Buffer.from('value_1_0')},
+          {key: 'key_1_1', value: Buffer.from('value_1_1')},
+        ],
+      },
+      {
+        owners: [{
+          owner: receiverCross,
+          pieces: 1,
+        }],
+        properties: [
+          {key: 'key_2_0', value: Buffer.from('value_2_0')},
+          {key: 'key_2_1', value: Buffer.from('value_2_1')},
+          {key: 'key_2_2', value: Buffer.from('value_2_2')},
+        ],
+      },
+    ]).send({from: caller});
+    const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
+    const bulkSize = 3;
+    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}`);
+    }
+
+    const properties = [
+      await contract.methods.properties(+nextTokenId, []).call(),
+      await contract.methods.properties(+nextTokenId + 1, []).call(),
+      await contract.methods.properties(+nextTokenId + 2, []).call(),
+    ];
+    expect(properties).to.be.deep.equal([
+      [
+        ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')],
+      ],
+      [
+        ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')],
+        ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')],
+      ],
+      [
+        ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
+        ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
+        ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
+      ],
+    ]);
+  });
+
+  itEth('Can perform mintBulkCross() with multiple owners', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const callerCross = helper.ethCrossAccount.fromAddress(caller);
+    const receiver = helper.eth.createAccount();
+    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
+    const receiver2 = helper.eth.createAccount();
+    const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);
+
+    const permissions = [
+      {code: TokenPermissionField.Mutable, value: true},
+      {code: TokenPermissionField.TokenOwner, value: true},
+      {code: TokenPermissionField.CollectionAdmin, value: true},
+    ];
+    const {collectionAddress} = await helper.eth.createCollection(
+      caller,
+      {
+        ...CREATE_COLLECTION_DATA_DEFAULTS,
+        name: 'A',
+        description: 'B',
+        tokenPrefix: 'C',
+        collectionMode: 'rft',
+        adminList: [callerCross],
+        tokenPropertyPermissions: [
+          {key: 'key_2_0', permissions},
+          {key: 'key_2_1', permissions},
+          {key: 'key_2_2', permissions},
+        ],
+      },
+    ).send();
+
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+    const nextTokenId = await contract.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+    const result = await contract.methods.mintBulkCross([{
+      owners: [
         {
-          owners: [{
-            owner: receiverCross,
-            pieces: 1,
-          }],
-          properties: [
-            {key: 'key_0_0', value: Buffer.from('value_0_0')},
-          ],
+          owner: receiverCross,
+          pieces: 1,
         },
         {
-          owners: [{
-            owner: receiverCross,
-            pieces: 2,
-          }],
-          properties: [
-            {key: 'key_1_0', value: Buffer.from('value_1_0')},
-            {key: 'key_1_1', value: Buffer.from('value_1_1')},
-          ],
+          owner: receiver2Cross,
+          pieces: 2,
         },
-        {
-          owners: [
-            {
-              owner: receiverCross,
-              pieces: 1,
-            },
-            {
-              owner: receiver2Cross,
-              pieces: 2,
-            },
-          ],
-          properties: [
-            {key: 'key_2_0', value: Buffer.from('value_2_0')},
-            {key: 'key_2_1', value: Buffer.from('value_2_1')},
-            {key: 'key_2_2', value: Buffer.from('value_2_2')},
-          ],
-        },
-      ]).send({from: caller});
-      const events = result.events.Transfer.sort((a: any, b: any) => +a.returnValues.tokenId - b.returnValues.tokenId);
-      const bulkSize = 3;
-      for(let i = 0; i < bulkSize; i++) {
-        const event = events[i];
-        expect(event.address).to.equal(collectionAddress);
-        expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
-        if(i == 0 || i == 1)
-          expect(event.returnValues.to).to.equal(receiver);
-        else
-          expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
-
-        expect(event.returnValues.tokenId).to.equal(`${+nextTokenId + i}`);
-      }
+      ],
+      properties: [
+        {key: 'key_2_0', value: Buffer.from('value_2_0')},
+        {key: 'key_2_1', value: Buffer.from('value_2_1')},
+        {key: 'key_2_2', value: Buffer.from('value_2_2')},
+      ],
+    }]).send({from: caller});
+    const event = result.events.Transfer;
+    expect(event.address).to.equal(collectionAddress);
+    expect(event.returnValues.from).to.equal('0x0000000000000000000000000000000000000000');
+    expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
+    expect(event.returnValues.tokenId).to.equal(`${+nextTokenId}`);
 
-      const properties = [
-        await contract.methods.properties(+nextTokenId, []).call(),
-        await contract.methods.properties(+nextTokenId + 1, []).call(),
-        await contract.methods.properties(+nextTokenId + 2, []).call(),
-      ];
-      expect(properties).to.be.deep.equal([
-        [
-          ['key_0_0', helper.getWeb3().utils.toHex('value_0_0')],
-        ],
-        [
-          ['key_1_0', helper.getWeb3().utils.toHex('value_1_0')],
-          ['key_1_1', helper.getWeb3().utils.toHex('value_1_1')],
-        ],
-        [
-          ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
-          ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
-          ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
-        ],
-      ]);
-    }
+    const properties = [
+      await contract.methods.properties(+nextTokenId, []).call(),
+    ];
+    expect(properties).to.be.deep.equal([[
+      ['key_2_0', helper.getWeb3().utils.toHex('value_2_0')],
+      ['key_2_1', helper.getWeb3().utils.toHex('value_2_1')],
+      ['key_2_2', helper.getWeb3().utils.toHex('value_2_2')],
+    ]]);
   });
 
   itEth('Can perform setApprovalForAll()', async ({helper}) => {
@@ -898,4 +950,70 @@
 
     await expect(contract.methods.transferFromCross(ownerCross, recieverCross, token.tokenId).send({from: spender})).to.be.rejected;
   });
+
+  itEth('[negative] Can perform mintBulkCross() with multiple owners and multiple tokens', async ({helper}) => {
+    const caller = await helper.eth.createAccountWithBalance(donor);
+    const callerCross = helper.ethCrossAccount.fromAddress(caller);
+    const receiver = helper.eth.createAccount();
+    const receiverCross = helper.ethCrossAccount.fromAddress(receiver);
+    const receiver2 = helper.eth.createAccount();
+    const receiver2Cross = helper.ethCrossAccount.fromAddress(receiver2);
+
+    const permissions = [
+      {code: TokenPermissionField.Mutable, value: true},
+      {code: TokenPermissionField.TokenOwner, value: true},
+      {code: TokenPermissionField.CollectionAdmin, value: true},
+    ];
+    const {collectionAddress} = await helper.eth.createCollection(
+      caller,
+      {
+        ...CREATE_COLLECTION_DATA_DEFAULTS,
+        name: 'A',
+        description: 'B',
+        tokenPrefix: 'C',
+        collectionMode: 'rft',
+        adminList: [callerCross],
+        tokenPropertyPermissions: [
+          {key: 'key_0_0', permissions},
+          {key: 'key_2_0', permissions},
+          {key: 'key_2_1', permissions},
+          {key: 'key_2_2', permissions},
+        ],
+      },
+    ).send();
+
+    const contract = await helper.ethNativeContract.collection(collectionAddress, 'rft', caller);
+    const nextTokenId = await contract.methods.nextTokenId().call();
+    expect(nextTokenId).to.be.equal('1');
+    const createData = [
+      {
+        owners: [{
+          owner: receiverCross,
+          pieces: 1,
+        }],
+        properties: [
+          {key: 'key_0_0', value: Buffer.from('value_0_0')},
+        ],
+      },
+      {
+        owners: [
+          {
+            owner: receiverCross,
+            pieces: 1,
+          },
+          {
+            owner: receiver2Cross,
+            pieces: 2,
+          },
+        ],
+        properties: [
+          {key: 'key_2_0', value: Buffer.from('value_2_0')},
+          {key: 'key_2_1', value: Buffer.from('value_2_1')},
+          {key: 'key_2_2', value: Buffer.from('value_2_2')},
+        ],
+      },
+    ];
+
+    await expect(contract.methods.mintBulkCross(createData).call({from: caller})).to.be.rejectedWith('creation of multiple tokens supported only if they have single owner each');
+  });
 });