Skip to content

Commit

Permalink
fix: update weighting for mintBulkCross and forbid multiple owners + …
Browse files Browse the repository at this point in the history
…multipe tokens
  • Loading branch information
Grigoriy Simonov committed Sep 22, 2023
1 parent 0c121c7 commit 752e2b0
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 74 deletions.
14 changes: 13 additions & 1 deletion pallets/refungible/src/erc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,11 @@ where

/// @notice Function to mint a token.
/// @param tokenProperties Properties of minted token
#[weight(<SelfWeightOf<T>>::create_multiple_items(token_properties.len() as u32) + <SelfWeightOf<T>>::set_token_properties(token_properties.len() as u32))]
#[weight(if token_properties.len() == 1 {
<SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(token_properties.iter().next().unwrap().owners.len() as u32)
} else {
<SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(token_properties.len() as u32)
} + <SelfWeightOf<T>>::set_token_properties(token_properties.len() as u32))]
fn mint_bulk_cross(
&mut self,
caller: Caller,
Expand All @@ -1051,9 +1055,17 @@ where
let budget = self
.recorder
.weight_calls_budget(<StructureWeight<T>>::find_parent());
let has_multiple_tokens = token_properties.len() > 1;

let mut create_rft_data = Vec::with_capacity(token_properties.len());
for MintTokenData { owners, properties } in token_properties {
let has_multiple_owners = owners.len() > 1;
if has_multiple_tokens & has_multiple_owners {
return Err(
"creation of multiple tokens supported only if they have single owner each"
.into(),
);
}
let users: BoundedBTreeMap<_, _, _> = owners
.into_iter()
.map(|data| Ok((data.owner.into_sub_cross_account::<T>()?, data.pieces)))
Expand Down
264 changes: 191 additions & 73 deletions tests/src/eth/reFungible.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,11 @@ describe('Refungible: Plain calls', () => {
}
});

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},
Expand Down Expand Up @@ -160,81 +158,135 @@ describe('Refungible: Plain calls', () => {
).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: [{
owner: receiverCross,
pieces: 1,
}],
properties: [
{key: 'key_0_0', value: Buffer.from('value_0_0')},
],
},
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: 2,
}],
properties: [
{key: 'key_1_0', value: Buffer.from('value_1_0')},
{key: 'key_1_1', value: Buffer.from('value_1_1')},
],
owner: receiverCross,
pieces: 1,
},
{
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')},
],
owner: receiver2Cross,
pieces: 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}) => {
Expand Down Expand Up @@ -898,4 +950,70 @@ describe('Negative tests', () => {

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');
});
});

0 comments on commit 752e2b0

Please sign in to comment.