difftreelog
added `collectionNestingPermissions` funtion in `Collection` interface
in: master
13 files changed
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -26,7 +26,7 @@
weight,
};
use pallet_evm_coder_substrate::dispatch_to_evm;
-use sp_std::vec::Vec;
+use sp_std::{vec, vec::Vec};
use up_data_structs::{
AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,
SponsoringRateLimit, SponsorshipState,
@@ -35,7 +35,9 @@
use crate::{
Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,
- eth::{EthCrossAccount, convert_cross_account_to_uint256},
+ eth::{
+ EthCrossAccount, convert_cross_account_to_uint256, CollectionPermissions as EvmPermissions,
+ },
weights::WeightInfo,
};
@@ -508,6 +510,14 @@
))
}
+ /// Returns permissions for a collection
+ fn collection_nesting_permissions(&self) -> Result<Vec<(EvmPermissions, bool)>> {
+ let nesting = self.collection.permissions.nesting();
+ Ok(vec![
+ (EvmPermissions::CollectionAdmin, nesting.collection_admin),
+ (EvmPermissions::TokenOwner, nesting.token_owner),
+ ])
+ }
/// Set the collection access method.
/// @param mode Access mode
/// 0 for Normal
pallets/common/src/eth.rsdiffbeforeafterboth--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -155,3 +155,10 @@
}
}
}
+#[derive(Default, Debug, Clone, Copy, AbiCoder)]
+#[repr(u8)]
+pub enum CollectionPermissions {
+ #[default]
+ CollectionAdmin,
+ TokenOwner,
+}
pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -18,7 +18,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xeecfdb34
+/// @dev the ERC-165 identifier for this interface is 0xb5e1747f
contract Collection is Dummy, ERC165 {
// /// Set collection property.
// ///
@@ -263,6 +263,15 @@
return Tuple21(false, new uint256[](0));
}
+ /// Returns permissions for a collection
+ /// @dev EVM selector for this function is: 0x5b2eaf4b,
+ /// or in textual repr: collectionNestingPermissions()
+ function collectionNestingPermissions() public view returns (Tuple24[] memory) {
+ require(false, stub_error);
+ dummy;
+ return new Tuple24[](0);
+ }
+
/// Set the collection access method.
/// @param mode Access mode
/// 0 for Normal
@@ -434,6 +443,17 @@
uint256 sub;
}
+enum CollectionPermissions {
+ CollectionAdmin,
+ TokenOwner
+}
+
+/// @dev anonymous struct
+struct Tuple24 {
+ CollectionPermissions field_0;
+ bool field_1;
+}
+
/// @dev anonymous struct
struct Tuple21 {
bool field_0;
pallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -119,7 +119,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xeecfdb34
+/// @dev the ERC-165 identifier for this interface is 0xb5e1747f
contract Collection is Dummy, ERC165 {
// /// Set collection property.
// ///
@@ -364,6 +364,15 @@
return Tuple34(false, new uint256[](0));
}
+ /// Returns permissions for a collection
+ /// @dev EVM selector for this function is: 0x5b2eaf4b,
+ /// or in textual repr: collectionNestingPermissions()
+ function collectionNestingPermissions() public view returns (Tuple37[] memory) {
+ require(false, stub_error);
+ dummy;
+ return new Tuple37[](0);
+ }
+
/// Set the collection access method.
/// @param mode Access mode
/// 0 for Normal
@@ -535,6 +544,17 @@
uint256 sub;
}
+enum CollectionPermissions {
+ CollectionAdmin,
+ TokenOwner
+}
+
+/// @dev anonymous struct
+struct Tuple37 {
+ CollectionPermissions field_0;
+ bool field_1;
+}
+
/// @dev anonymous struct
struct Tuple34 {
bool field_0;
pallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -119,7 +119,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xeecfdb34
+/// @dev the ERC-165 identifier for this interface is 0xb5e1747f
contract Collection is Dummy, ERC165 {
// /// Set collection property.
// ///
@@ -364,6 +364,15 @@
return Tuple33(false, new uint256[](0));
}
+ /// Returns permissions for a collection
+ /// @dev EVM selector for this function is: 0x5b2eaf4b,
+ /// or in textual repr: collectionNestingPermissions()
+ function collectionNestingPermissions() public view returns (Tuple36[] memory) {
+ require(false, stub_error);
+ dummy;
+ return new Tuple36[](0);
+ }
+
/// Set the collection access method.
/// @param mode Access mode
/// 0 for Normal
@@ -535,6 +544,17 @@
uint256 sub;
}
+enum CollectionPermissions {
+ CollectionAdmin,
+ TokenOwner
+}
+
+/// @dev anonymous struct
+struct Tuple36 {
+ CollectionPermissions field_0;
+ bool field_1;
+}
+
/// @dev anonymous struct
struct Tuple33 {
bool field_0;
tests/src/eth/abi/fungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/fungible.json
+++ b/tests/src/eth/abi/fungible.json
@@ -208,6 +208,27 @@
},
{
"inputs": [],
+ "name": "collectionNestingPermissions",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "enum CollectionPermissions",
+ "name": "field_0",
+ "type": "uint8"
+ },
+ { "internalType": "bool", "name": "field_1", "type": "bool" }
+ ],
+ "internalType": "struct Tuple24[]",
+ "name": "",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
"name": "collectionNestingRestrictedCollectionIds",
"outputs": [
{
tests/src/eth/abi/nonFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -238,6 +238,27 @@
},
{
"inputs": [],
+ "name": "collectionNestingPermissions",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "enum CollectionPermissions",
+ "name": "field_0",
+ "type": "uint8"
+ },
+ { "internalType": "bool", "name": "field_1", "type": "bool" }
+ ],
+ "internalType": "struct Tuple37[]",
+ "name": "",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
"name": "collectionNestingRestrictedCollectionIds",
"outputs": [
{
tests/src/eth/abi/reFungible.jsondiffbeforeafterboth--- a/tests/src/eth/abi/reFungible.json
+++ b/tests/src/eth/abi/reFungible.json
@@ -220,6 +220,27 @@
},
{
"inputs": [],
+ "name": "collectionNestingPermissions",
+ "outputs": [
+ {
+ "components": [
+ {
+ "internalType": "enum CollectionPermissions",
+ "name": "field_0",
+ "type": "uint8"
+ },
+ { "internalType": "bool", "name": "field_1", "type": "bool" }
+ ],
+ "internalType": "struct Tuple36[]",
+ "name": "",
+ "type": "tuple[]"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "inputs": [],
"name": "collectionNestingRestrictedCollectionIds",
"outputs": [
{
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -13,7 +13,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xeecfdb34
+/// @dev the ERC-165 identifier for this interface is 0xb5e1747f
interface Collection is Dummy, ERC165 {
// /// Set collection property.
// ///
@@ -171,6 +171,11 @@
/// or in textual repr: collectionNestingRestrictedCollectionIds()
function collectionNestingRestrictedCollectionIds() external view returns (Tuple20 memory);
+ /// Returns permissions for a collection
+ /// @dev EVM selector for this function is: 0x5b2eaf4b,
+ /// or in textual repr: collectionNestingPermissions()
+ function collectionNestingPermissions() external view returns (Tuple23[] memory);
+
/// Set the collection access method.
/// @param mode Access mode
/// 0 for Normal
@@ -284,6 +289,17 @@
}
/// @dev anonymous struct
+struct Tuple23 {
+ CollectionPermissions field_0;
+ bool field_1;
+}
+
+enum CollectionPermissions {
+ CollectionAdmin,
+ TokenOwner
+}
+
+/// @dev anonymous struct
struct Tuple20 {
bool field_0;
uint256[] field_1;
tests/src/eth/api/UniqueNFT.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -80,7 +80,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xeecfdb34
+/// @dev the ERC-165 identifier for this interface is 0xb5e1747f
interface Collection is Dummy, ERC165 {
// /// Set collection property.
// ///
@@ -238,6 +238,11 @@
/// or in textual repr: collectionNestingRestrictedCollectionIds()
function collectionNestingRestrictedCollectionIds() external view returns (Tuple31 memory);
+ /// Returns permissions for a collection
+ /// @dev EVM selector for this function is: 0x5b2eaf4b,
+ /// or in textual repr: collectionNestingPermissions()
+ function collectionNestingPermissions() external view returns (Tuple34[] memory);
+
/// Set the collection access method.
/// @param mode Access mode
/// 0 for Normal
@@ -351,6 +356,17 @@
}
/// @dev anonymous struct
+struct Tuple34 {
+ CollectionPermissions field_0;
+ bool field_1;
+}
+
+enum CollectionPermissions {
+ CollectionAdmin,
+ TokenOwner
+}
+
+/// @dev anonymous struct
struct Tuple31 {
bool field_0;
uint256[] field_1;
tests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -80,7 +80,7 @@
}
/// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xeecfdb34
+/// @dev the ERC-165 identifier for this interface is 0xb5e1747f
interface Collection is Dummy, ERC165 {
// /// Set collection property.
// ///
@@ -238,6 +238,11 @@
/// or in textual repr: collectionNestingRestrictedCollectionIds()
function collectionNestingRestrictedCollectionIds() external view returns (Tuple30 memory);
+ /// Returns permissions for a collection
+ /// @dev EVM selector for this function is: 0x5b2eaf4b,
+ /// or in textual repr: collectionNestingPermissions()
+ function collectionNestingPermissions() external view returns (Tuple33[] memory);
+
/// Set the collection access method.
/// @param mode Access mode
/// 0 for Normal
@@ -351,6 +356,17 @@
}
/// @dev anonymous struct
+struct Tuple33 {
+ CollectionPermissions field_0;
+ bool field_1;
+}
+
+enum CollectionPermissions {
+ CollectionAdmin,
+ TokenOwner
+}
+
+/// @dev anonymous struct
struct Tuple30 {
bool field_0;
uint256[] field_1;
tests/src/eth/nesting/nest.test.tsdiffbeforeafterboth1import {IKeyringPair} from '@polkadot/types/types';2import {Contract} from 'web3-eth-contract';34import {itEth, EthUniqueHelper, usingEthPlaygrounds, expect} from '../util';56const createNestingCollection = async (7 helper: EthUniqueHelper,8 owner: string,9): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {10 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');1112 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13 await contract.methods.setCollectionNesting(true).send({from: owner});1415 return {collectionId, collectionAddress, contract};16};171819describe('EVM nesting tests group', () => {20 let donor: IKeyringPair;2122 before(async function() {23 await usingEthPlaygrounds(async (_, privateKey) => {24 donor = await privateKey({filename: __filename});25 });26 });2728 describe('Integration Test: EVM Nesting', () => {29 itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {30 const owner = await helper.eth.createAccountWithBalance(donor);31 const {collectionId, contract} = await createNestingCollection(helper, owner);3233 // Create a token to be nested to34 const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});35 const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;36 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);3738 // Create a nested token39 const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});40 const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;41 expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);4243 // Create a token to be nested and nest44 const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});45 const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;4647 await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});48 expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);4950 // Unnest token back51 await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});52 expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);53 });54 55 itEth('NFT: collectionNestingRestrictedCollectionIds()', async ({helper}) => {56 const owner = await helper.eth.createAccountWithBalance(donor);57 const {collectionId: unnestedCollsectionId, collectionAddress: unnsetedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');58 const unnestedContract = helper.ethNativeContract.collection(unnsetedCollectionAddress, 'nft', owner);59 expect(await unnestedContract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([false, []]);60 61 const {contract} = await createNestingCollection(helper, owner);62 expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, []]);63 await contract.methods.setCollectionNesting(true, [unnsetedCollectionAddress]).send({from: owner});64 expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, [unnestedCollsectionId.toString()]]);65 66 });67 68 itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {69 const owner = await helper.eth.createAccountWithBalance(donor);7071 const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);72 const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(helper, owner);73 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});7475 // Create a token to nest into76 const mintingtargetNftTokenIdResult = await contractA.methods.mint(owner).send({from: owner});77 const targetNftTokenId = mintingtargetNftTokenIdResult.events.Transfer.returnValues.tokenId;78 const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId);7980 // Create a token for nesting in the same collection as the target81 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});82 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;8384 // Create a token for nesting in a different collection85 const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});86 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;8788 // Nest89 await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner});90 expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1);9192 await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner});93 expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1);94 });95 });9697 describe('Negative Test: EVM Nesting', () => {98 itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {99 const owner = await helper.eth.createAccountWithBalance(donor);100101 const {collectionId, contract} = await createNestingCollection(helper, owner);102 await contract.methods.setCollectionNesting(false).send({from: owner});103104 // Create a token to nest into105 const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});106 const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;107 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);108109 // Create a token to nest110 const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});111 const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;112113 // Try to nest114 await expect(contract.methods115 .transfer(targetNftTokenAddress, nftTokenId)116 .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');117 });118119 itEth('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => {120 const owner = await helper.eth.createAccountWithBalance(donor);121 const malignant = await helper.eth.createAccountWithBalance(donor);122123 const {collectionId, contract} = await createNestingCollection(helper, owner);124125 // Mint a token126 const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});127 const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;128 const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);129130 // Mint a token belonging to a different account131 const mintingTokenIdResult = await contract.methods.mint(malignant).send({from: owner});132 const tokenId = mintingTokenIdResult.events.Transfer.returnValues.tokenId;133134 // Try to nest one token in another as a non-owner account135 await expect(contract.methods136 .transfer(targetTokenAddress, tokenId)137 .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');138 });139140 itEth('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => {141 const owner = await helper.eth.createAccountWithBalance(donor);142 const malignant = await helper.eth.createAccountWithBalance(donor);143144 const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);145 const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(helper, owner);146147 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});148149 // Create a token in one collection150 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});151 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;152 const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);153154 // Create a token in another collection155 const mintingTokenIdBResult = await contractB.methods.mint(malignant).send({from: owner});156 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;157158 // Try to drag someone else's token into the other collection and nest159 await expect(contractB.methods160 .transfer(nftTokenAddressA, nftTokenIdB)161 .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');162 });163164 itEth('NFT: disallows to nest token in an unlisted collection', async ({helper}) => {165 const owner = await helper.eth.createAccountWithBalance(donor);166167 const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);168 const {contract: contractB} = await createNestingCollection(helper, owner);169170 await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner});171172 // Create a token in one collection173 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});174 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;175 const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);176177 // Create a token in another collection178 const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});179 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;180181182 // Try to nest into a token in the other collection, disallowed in the first183 await expect(contractB.methods184 .transfer(nftTokenAddressA, nftTokenIdB)185 .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest');186 });187 });188});1import {IKeyringPair} from '@polkadot/types/types';2import {Contract} from 'web3-eth-contract';34import {itEth, EthUniqueHelper, usingEthPlaygrounds, expect} from '../util';56const createNestingCollection = async (7 helper: EthUniqueHelper,8 owner: string,9): Promise<{ collectionId: number, collectionAddress: string, contract: Contract }> => {10 const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');1112 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13 await contract.methods.setCollectionNesting(true).send({from: owner});1415 return {collectionId, collectionAddress, contract};16};171819describe('EVM nesting tests group', () => {20 let donor: IKeyringPair;2122 before(async function() {23 await usingEthPlaygrounds(async (_, privateKey) => {24 donor = await privateKey({filename: __filename});25 });26 });2728 describe('Integration Test: EVM Nesting', () => {29 itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {30 const owner = await helper.eth.createAccountWithBalance(donor);31 const {collectionId, contract} = await createNestingCollection(helper, owner);3233 // Create a token to be nested to34 const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});35 const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;36 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);3738 // Create a nested token39 const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});40 const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;41 expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);4243 // Create a token to be nested and nest44 const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});45 const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;4647 await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});48 expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);4950 // Unnest token back51 await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});52 expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);53 });54 55 itEth('NFT: collectionNestingRestrictedCollectionIds() & collectionNestingPermissions', async ({helper}) => {56 const owner = await helper.eth.createAccountWithBalance(donor);57 const {collectionId: unnestedCollsectionId, collectionAddress: unnsetedCollectionAddress} = await helper.eth.createNFTCollection(owner, 'A', 'B', 'C');58 const unnestedContract = helper.ethNativeContract.collection(unnsetedCollectionAddress, 'nft', owner);59 expect(await unnestedContract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([false, []]);60 61 const {contract} = await createNestingCollection(helper, owner);62 expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, []]);63 await contract.methods.setCollectionNesting(true, [unnsetedCollectionAddress]).send({from: owner});64 expect(await contract.methods.collectionNestingRestrictedCollectionIds().call({from: owner})).to.be.like([true, [unnestedCollsectionId.toString()]]);65 expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['0', false], ['1', true]]);66 await contract.methods.setCollectionNesting(false).send({from: owner});67 expect(await contract.methods.collectionNestingPermissions().call({from: owner})).to.be.like([['0', false], ['1', false]]);68 });69 70 itEth('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async ({helper}) => {71 const owner = await helper.eth.createAccountWithBalance(donor);7273 const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);74 const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(helper, owner);75 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});7677 // Create a token to nest into78 const mintingtargetNftTokenIdResult = await contractA.methods.mint(owner).send({from: owner});79 const targetNftTokenId = mintingtargetNftTokenIdResult.events.Transfer.returnValues.tokenId;80 const nftTokenAddressA1 = helper.ethAddress.fromTokenId(collectionIdA, targetNftTokenId);8182 // Create a token for nesting in the same collection as the target83 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});84 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;8586 // Create a token for nesting in a different collection87 const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});88 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;8990 // Nest91 await contractA.methods.transfer(nftTokenAddressA1, nftTokenIdA).send({from: owner});92 expect(await contractA.methods.ownerOf(nftTokenIdA).call()).to.be.equal(nftTokenAddressA1);9394 await contractB.methods.transfer(nftTokenAddressA1, nftTokenIdB).send({from: owner});95 expect(await contractB.methods.ownerOf(nftTokenIdB).call()).to.be.equal(nftTokenAddressA1);96 });97 });9899 describe('Negative Test: EVM Nesting', () => {100 itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {101 const owner = await helper.eth.createAccountWithBalance(donor);102103 const {collectionId, contract} = await createNestingCollection(helper, owner);104 await contract.methods.setCollectionNesting(false).send({from: owner});105106 // Create a token to nest into107 const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});108 const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;109 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);110111 // Create a token to nest112 const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});113 const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;114115 // Try to nest116 await expect(contract.methods117 .transfer(targetNftTokenAddress, nftTokenId)118 .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');119 });120121 itEth('NFT: disallows a non-Owner to nest someone else\'s token', async ({helper}) => {122 const owner = await helper.eth.createAccountWithBalance(donor);123 const malignant = await helper.eth.createAccountWithBalance(donor);124125 const {collectionId, contract} = await createNestingCollection(helper, owner);126127 // Mint a token128 const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});129 const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;130 const targetTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);131132 // Mint a token belonging to a different account133 const mintingTokenIdResult = await contract.methods.mint(malignant).send({from: owner});134 const tokenId = mintingTokenIdResult.events.Transfer.returnValues.tokenId;135136 // Try to nest one token in another as a non-owner account137 await expect(contract.methods138 .transfer(targetTokenAddress, tokenId)139 .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');140 });141142 itEth('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async ({helper}) => {143 const owner = await helper.eth.createAccountWithBalance(donor);144 const malignant = await helper.eth.createAccountWithBalance(donor);145146 const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);147 const {collectionAddress: collectionAddressB, contract: contractB} = await createNestingCollection(helper, owner);148149 await contractA.methods.setCollectionNesting(true, [collectionAddressA, collectionAddressB]).send({from: owner});150151 // Create a token in one collection152 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});153 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;154 const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);155156 // Create a token in another collection157 const mintingTokenIdBResult = await contractB.methods.mint(malignant).send({from: owner});158 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;159160 // Try to drag someone else's token into the other collection and nest161 await expect(contractB.methods162 .transfer(nftTokenAddressA, nftTokenIdB)163 .call({from: malignant})).to.be.rejectedWith('UserIsNotAllowedToNest');164 });165166 itEth('NFT: disallows to nest token in an unlisted collection', async ({helper}) => {167 const owner = await helper.eth.createAccountWithBalance(donor);168169 const {collectionId: collectionIdA, collectionAddress: collectionAddressA, contract: contractA} = await createNestingCollection(helper, owner);170 const {contract: contractB} = await createNestingCollection(helper, owner);171172 await contractA.methods.setCollectionNesting(true, [collectionAddressA]).send({from: owner});173174 // Create a token in one collection175 const mintingTokenIdAResult = await contractA.methods.mint(owner).send({from: owner});176 const nftTokenIdA = mintingTokenIdAResult.events.Transfer.returnValues.tokenId;177 const nftTokenAddressA = helper.ethAddress.fromTokenId(collectionIdA, nftTokenIdA);178179 // Create a token in another collection180 const mintingTokenIdBResult = await contractB.methods.mint(owner).send({from: owner});181 const nftTokenIdB = mintingTokenIdBResult.events.Transfer.returnValues.tokenId;182183184 // Try to nest into a token in the other collection, disallowed in the first185 await expect(contractB.methods186 .transfer(nftTokenAddressA, nftTokenIdB)187 .call()).to.be.rejectedWith('SourceCollectionIsNotAllowedToNest');188 });189 });190});