difftreelog
Merge pull request #384 from UniqueNetwork/fix/owner_can_tarnsfer
in: master
OwnerCanTransfer flag default to false
10 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1164,6 +1164,7 @@
old_limit: &CollectionLimits,
mut new_limit: CollectionLimits,
) -> Result<CollectionLimits, DispatchError> {
+ let limits = old_limit;
limit_default!(old_limit, new_limit,
account_token_ownership_limit => ensure!(
new_limit <= MAX_TOKEN_OWNERSHIP,
@@ -1190,6 +1191,7 @@
),
sponsor_approve_timeout => {},
owner_can_transfer => ensure!(
+ !limits.owner_can_transfer_instaled() ||
old_limit || !new_limit,
<Error<T>>::OwnerPermissionsCantBeReverted,
),
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -409,7 +409,10 @@
.min(MAX_SPONSOR_TIMEOUT)
}
pub fn owner_can_transfer(&self) -> bool {
- self.owner_can_transfer.unwrap_or(true)
+ self.owner_can_transfer.unwrap_or(false)
+ }
+ pub fn owner_can_transfer_instaled(&self) -> bool {
+ self.owner_can_transfer.is_some()
}
pub fn owner_can_destroy(&self) -> bool {
self.owner_can_destroy.unwrap_or(true)
tests/src/approve.test.tsdiffbeforeafterboth--- a/tests/src/approve.test.ts
+++ b/tests/src/approve.test.ts
@@ -28,7 +28,7 @@
setCollectionLimitsExpectSuccess,
transferExpectSuccess,
addCollectionAdminExpectSuccess,
- adminApproveFromExpectSuccess,
+ adminApproveFromExpectFail,
getCreatedCollectionCount,
transferFromExpectSuccess,
transferFromExpectFail,
@@ -84,11 +84,11 @@
await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);
});
- it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {
+ it('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {
const collectionId = await createCollectionExpectSuccess();
const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
- await adminApproveFromExpectSuccess(collectionId, itemId, alice, bob.address, charlie.address);
+ await adminApproveFromExpectFail(collectionId, itemId, alice, bob.address, charlie.address);
});
});
@@ -292,7 +292,7 @@
});
});
-describe('Administrator and collection owner do not need approval in order to execute TransferFrom:', () => {
+describe('Administrator and collection owner do not need approval in order to execute TransferFrom (with owner_can_transfer_flag = true):', () => {
let alice: IKeyringPair;
let bob: IKeyringPair;
let charlie: IKeyringPair;
@@ -309,6 +309,7 @@
it('NFT', async () => {
const collectionId = await createCollectionExpectSuccess();
+ await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});
const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', charlie.address);
await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'NFT');
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
@@ -317,6 +318,7 @@
it('Fungible up to an approved amount', async () => {
const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});
+ await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});
const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', charlie.address);
await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'Fungible');
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
@@ -325,6 +327,7 @@
it('ReFungible up to an approved amount', async () => {
const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});
+ await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});
const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', charlie.address);
await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'ReFungible');
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
@@ -402,7 +405,7 @@
const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
- await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);
+ await adminApproveFromExpectFail(collectionId, itemId, bob, alice.address, charlie.address);
});
});
tests/src/burnItem.test.tsdiffbeforeafterboth--- a/tests/src/burnItem.test.ts
+++ b/tests/src/burnItem.test.ts
@@ -23,6 +23,7 @@
normalizeAccountId,
addCollectionAdminExpectSuccess,
getBalance,
+ setCollectionLimitsExpectSuccess,
isTokenExists,
} from './util/helpers';
@@ -149,6 +150,7 @@
it('Burn item in NFT collection', async () => {
const createMode = 'NFT';
const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});
+ await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});
const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
@@ -167,6 +169,7 @@
it('Burn item in Fungible collection', async () => {
const createMode = 'Fungible';
const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
+ await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});
const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); // Helper creates 10 fungible tokens
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
@@ -189,6 +192,7 @@
it('Burn item in ReFungible collection', async () => {
const createMode = 'ReFungible';
const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});
+ await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});
const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);
await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
tests/src/eth/crossTransfer.test.tsdiffbeforeafterboth--- a/tests/src/eth/crossTransfer.test.ts
+++ b/tests/src/eth/crossTransfer.test.ts
@@ -18,6 +18,7 @@
createFungibleItemExpectSuccess,
transferExpectSuccess,
transferFromExpectSuccess,
+ setCollectionLimitsExpectSuccess,
createItemExpectSuccess} from '../util/helpers';
import {collectionIdToAddress,
createEthAccountWithBalance,
@@ -35,6 +36,7 @@
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');
@@ -48,6 +50,7 @@
});
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);
@@ -71,6 +74,7 @@
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');
@@ -85,6 +89,7 @@
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});
tests/src/limits.test.tsdiffbeforeafterboth--- a/tests/src/limits.test.ts
+++ b/tests/src/limits.test.ts
@@ -406,6 +406,7 @@
it('Effective collection limits', async () => {
await usingApi(async (api) => {
const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+ await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});
{ // Check that limits is undefined
const collection = await api.rpc.unique.collectionById(collectionId);
@@ -419,7 +420,7 @@
expect(limits.tokenLimit.toHuman()).to.be.null;
expect(limits.sponsorTransferTimeout.toHuman()).to.be.null;
expect(limits.sponsorApproveTimeout.toHuman()).to.be.null;
- expect(limits.ownerCanTransfer.toHuman()).to.be.null;
+ expect(limits.ownerCanTransfer.toHuman()).to.be.true;
expect(limits.ownerCanDestroy.toHuman()).to.be.null;
expect(limits.transfersEnabled.toHuman()).to.be.null;
}
tests/src/nesting/graphs.test.tsdiffbeforeafterboth--- a/tests/src/nesting/graphs.test.ts
+++ b/tests/src/nesting/graphs.test.ts
@@ -3,7 +3,7 @@
import {expect} from 'chai';
import {tokenIdToCross} from '../eth/util/helpers';
import usingApi, {executeTransaction} from '../substrate/substrate-api';
-import {getCreateCollectionResult, transferExpectSuccess} from '../util/helpers';
+import {getCreateCollectionResult, transferExpectSuccess, setCollectionLimitsExpectSuccess} from '../util/helpers';
/**
* ```dot
@@ -36,6 +36,7 @@
await usingApi(async (api, privateKeyWrapper) => {
const alice = privateKeyWrapper('//Alice');
const collection = await buildComplexObjectGraph(api, alice);
+ await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});
// to self
await expect(
tests/src/nesting/nest.test.tsdiffbeforeafterboth1import {expect} from 'chai';2import {tokenIdToAddress} from '../eth/util/helpers';3import usingApi, {executeTransaction} from '../substrate/substrate-api';4import {5 addCollectionAdminExpectSuccess,6 addToAllowListExpectSuccess,7 createCollectionExpectSuccess,8 createItemExpectSuccess,9 enableAllowListExpectSuccess,10 enablePublicMintingExpectSuccess,11 getTokenChildren,12 getTokenOwner,13 getTopmostTokenOwner,14 normalizeAccountId,15 setCollectionPermissionsExpectSuccess,16 transferExpectFailure,17 transferExpectSuccess,18 transferFromExpectSuccess,19} from '../util/helpers';20import {IKeyringPair} from '@polkadot/types/types';2122let alice: IKeyringPair;23let bob: IKeyringPair;24let charlie: IKeyringPair;2526describe('Integration Test: Composite nesting tests', () => {27 before(async () => {28 await usingApi(async (_, privateKeyWrapper) => {29 alice = privateKeyWrapper('//Alice');30 bob = privateKeyWrapper('//Bob');31 });32 });3334 it('Performs the full suite: bundles a token, transfers, and unnests', async () => {35 await usingApi(async api => {36 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});37 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});38 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');3940 // Create a nested token41 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});42 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});43 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});4445 // Create a token to be nested46 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');4748 // Nest49 await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});50 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});51 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});5253 // Move bundle to different user54 await transferExpectSuccess(collection, targetToken, alice, {Substrate: bob.address});55 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: bob.address});56 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});5758 // Unnest59 await transferFromExpectSuccess(collection, newToken, bob, {Ethereum: tokenIdToAddress(collection, targetToken)}, {Substrate: bob.address});60 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});61 });62 });6364 it('Transfers an already bundled token', async () => {65 await usingApi(async api => {66 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});67 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});6869 const tokenA = await createItemExpectSuccess(alice, collection, 'NFT');70 const tokenB = await createItemExpectSuccess(alice, collection, 'NFT');7172 // Create a nested token73 const tokenC = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, tokenA)});74 expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});75 expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenA).toLowerCase()});7677 // Transfer the nested token to another token78 await expect(executeTransaction(79 api,80 alice,81 api.tx.unique.transferFrom(82 normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenA)}),83 normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenB)}),84 collection,85 tokenC,86 1,87 ),88 )).to.not.be.rejected;89 expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});90 expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenB).toLowerCase()});91 });92 });9394 it('Checks token children', async () => {95 await usingApi(async api => {96 const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});97 await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {tokenOwner: true}});98 const collectionB = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});99100 const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT');101 const targetAddress = {Ethereum: tokenIdToAddress(collectionA, targetToken)};102 let children = await getTokenChildren(api, collectionA, targetToken);103 expect(children.length).to.be.equal(0, 'Children length check at creation');104105 // Create a nested NFT token106 const tokenA = await createItemExpectSuccess(alice, collectionA, 'NFT', targetAddress);107 children = await getTokenChildren(api, collectionA, targetToken);108 expect(children.length).to.be.equal(1, 'Children length check at nesting #1');109 expect(children).to.have.deep.members([110 {token: tokenA, collection: collectionA},111 ], 'Children contents check at nesting #1');112113 // Create then nest114 const tokenB = await createItemExpectSuccess(alice, collectionA, 'NFT');115 await transferExpectSuccess(collectionA, tokenB, alice, targetAddress);116 children = await getTokenChildren(api, collectionA, targetToken);117 expect(children.length).to.be.equal(2, 'Children length check at nesting #2');118 expect(children).to.have.deep.members([119 {token: tokenA, collection: collectionA},120 {token: tokenB, collection: collectionA},121 ], 'Children contents check at nesting #2');122123 // Move token B to a different user outside the nesting tree124 await transferExpectSuccess(collectionA, tokenB, alice, bob);125 children = await getTokenChildren(api, collectionA, targetToken);126 expect(children.length).to.be.equal(1, 'Children length check at unnesting');127 expect(children).to.be.have.deep.members([128 {token: tokenA, collection: collectionA},129 ], 'Children contents check at unnesting');130131 // Create a fungible token in another collection and then nest132 const tokenC = await createItemExpectSuccess(alice, collectionB, 'Fungible');133 await transferExpectSuccess(collectionB, tokenC, alice, targetAddress, 1, 'Fungible');134 children = await getTokenChildren(api, collectionA, targetToken);135 expect(children.length).to.be.equal(2, 'Children length check at nesting #3 (from another collection)');136 expect(children).to.be.have.deep.members([137 {token: tokenA, collection: collectionA},138 {token: tokenC, collection: collectionB},139 ], 'Children contents check at nesting #3 (from another collection)');140141 // Move the fungible token inside token A deeper in the nesting tree142 await transferFromExpectSuccess(collectionB, tokenC, alice, targetAddress, {Ethereum: tokenIdToAddress(collectionA, tokenA)}, 1, 'Fungible');143 children = await getTokenChildren(api, collectionA, targetToken);144 expect(children.length).to.be.equal(1, 'Children length check at deeper nesting');145 expect(children).to.be.have.deep.members([146 {token: tokenA, collection: collectionA},147 ], 'Children contents check at deeper nesting');148 });149 });150});151152describe('Integration Test: Various token type nesting', async () => {153 before(async () => {154 await usingApi(async (_, privateKeyWrapper) => {155 alice = privateKeyWrapper('//Alice');156 bob = privateKeyWrapper('//Bob');157 charlie = privateKeyWrapper('//Charlie');158 });159 });160161 it('Admin (NFT): allows an Admin to nest a token', async () => {162 await usingApi(async api => {163 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});164 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});165 await addCollectionAdminExpectSuccess(alice, collection, bob.address);166 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);167168 // Create a nested token169 const nestedToken = await createItemExpectSuccess(bob, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});170 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: charlie.address});171 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});172173 // Create a token to be nested and nest174 const newToken = await createItemExpectSuccess(bob, collection, 'NFT');175 await transferExpectSuccess(collection, newToken, bob, {Ethereum: tokenIdToAddress(collection, targetToken)});176 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: charlie.address});177 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});178 });179 });180181 it('Admin (NFT): Admin and Token Owner can operate together', async () => {182 await usingApi(async api => {183 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});184 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, collectionAdmin: true}});185 await addCollectionAdminExpectSuccess(alice, collection, bob.address);186 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);187188 // Create a nested token by an administrator189 const nestedToken = await createItemExpectSuccess(bob, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});190 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: charlie.address});191 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});192193 // Create a token and allow the owner to nest too194 const newToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);195 await transferExpectSuccess(collection, newToken, charlie, {Ethereum: tokenIdToAddress(collection, nestedToken)});196 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: charlie.address});197 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, nestedToken).toLowerCase()});198 });199 });200201 it('Admin (NFT): allows an Admin to nest a token (Restricted nesting)', async () => {202 await usingApi(async api => {203 const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});204 await addCollectionAdminExpectSuccess(alice, collectionA, bob.address);205 const collectionB = await createCollectionExpectSuccess({mode: {type: 'NFT'}});206 await addCollectionAdminExpectSuccess(alice, collectionB, bob.address);207 await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {collectionAdmin: true, restricted:[collectionA, collectionB]}});208 const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT', charlie.address);209210 // Create a nested token211 const nestedToken = await createItemExpectSuccess(bob, collectionB, 'NFT', {Ethereum: tokenIdToAddress(collectionA, targetToken)});212 expect(await getTopmostTokenOwner(api, collectionB, nestedToken)).to.be.deep.equal({Substrate: charlie.address});213 expect(await getTokenOwner(api, collectionB, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collectionA, targetToken).toLowerCase()});214215 // Create a token to be nested and nest216 const newToken = await createItemExpectSuccess(bob, collectionB, 'NFT');217 await transferExpectSuccess(collectionB, newToken, bob, {Ethereum: tokenIdToAddress(collectionA, targetToken)});218 expect(await getTopmostTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Substrate: charlie.address});219 expect(await getTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collectionA, targetToken).toLowerCase()});220 });221 });222223 // ---------- Non-Fungible ----------224225 it('NFT: allows an Owner to nest/unnest their token', async () => {226 await usingApi(async api => {227 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});228 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});229 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');230231 // Create a nested token232 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});233 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});234 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});235236 // Create a token to be nested and nest237 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');238 await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});239 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});240 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});241 });242 });243244 it('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {245 await usingApi(async api => {246 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});247 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[collection]}});248 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');249250 // Create a nested token251 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});252 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});253 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});254255 // Create a token to be nested and nest256 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');257 await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});258 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});259 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});260 });261 });262263 // ---------- Fungible ----------264265 it('Fungible: allows an Owner to nest/unnest their token', async () => {266 await usingApi(async api => {267 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});268 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});269 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});270 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};271272 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});273274 // Create a nested token275 await expect(executeTransaction(api, alice, api.tx.unique.createItem(276 collectionFT,277 targetAddress,278 {Fungible: {Value: 10}},279 ))).to.not.be.rejected;280281 // Nest a new token282 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');283 await transferExpectSuccess(collectionFT, newToken, alice, targetAddress, 1, 'Fungible');284 });285 });286287 it('Fungible: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {288 await usingApi(async api => {289 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});290 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});291 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};292293 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});294295 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted: [collectionFT]}});296297 // Create a nested token298 await expect(executeTransaction(api, alice, api.tx.unique.createItem(299 collectionFT,300 targetAddress,301 {Fungible: {Value: 10}},302 ))).to.not.be.rejected;303304 // Nest a new token305 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');306 await transferExpectSuccess(collectionFT, newToken, alice, targetAddress, 1, 'Fungible');307 });308 });309310 // ---------- Re-Fungible ----------311312 it('ReFungible: allows an Owner to nest/unnest their token', async () => {313 await usingApi(async api => {314 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});315 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});316 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});317 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};318319 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});320321 // Create a nested token322 await expect(executeTransaction(api, alice, api.tx.unique.createItem(323 collectionRFT,324 targetAddress,325 {ReFungible: {pieces: 100}},326 ))).to.not.be.rejected;327328 // Nest a new token329 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');330 await transferExpectSuccess(collectionRFT, newToken, alice, targetAddress, 100, 'ReFungible');331 });332 });333334 it('ReFungible: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {335 await usingApi(async api => {336 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});337 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});338 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};339340 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});341342 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionRFT]}});343344 // Create a nested token345 await expect(executeTransaction(api, alice, api.tx.unique.createItem(346 collectionRFT,347 targetAddress,348 {ReFungible: {pieces: 100}},349 ))).to.not.be.rejected;350351 // Nest a new token352 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');353 await transferExpectSuccess(collectionRFT, newToken, alice, targetAddress, 100, 'ReFungible');354 });355 });356});357358describe('Negative Test: Nesting', async() => {359 before(async () => {360 await usingApi(async (_, privateKeyWrapper) => {361 alice = privateKeyWrapper('//Alice');362 bob = privateKeyWrapper('//Bob');363 });364 });365366 it('Disallows excessive token nesting', async () => {367 await usingApi(async api => {368 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});369 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});370 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');371372 const maxNestingLevel = 5;373 let prevToken = targetToken;374375 // Create a nested-token matryoshka376 for (let i = 0; i < maxNestingLevel; i++) {377 const nestedToken = await createItemExpectSuccess(378 alice,379 collection,380 'NFT',381 {Ethereum: tokenIdToAddress(collection, prevToken)},382 );383384 prevToken = nestedToken;385 }386387 // The nesting depth is limited by `maxNestingLevel`388 await expect(executeTransaction(api, alice, api.tx.unique.createItem(389 collection,390 {Ethereum: tokenIdToAddress(collection, prevToken)},391 {nft: {}} as any,392 )), 'while creating nested token').to.be.rejectedWith(/^structure\.DepthLimit$/);393394 expect(await getTopmostTokenOwner(api, collection, prevToken)).to.be.deep.equal({Substrate: alice.address});395 });396 });397398 // ---------- Admin ------------399400 it('Admin (NFT): disallows an Admin to operate nesting when only TokenOwner is allowed', async () => {401 await usingApi(async api => {402 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});403 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});404 await addCollectionAdminExpectSuccess(alice, collection, bob.address);405 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');406407 // Try to create a nested token as collection admin when it's disallowed408 await expect(executeTransaction(api, bob, api.tx.unique.createItem(409 collection,410 {Ethereum: tokenIdToAddress(collection, targetToken)},411 {nft: {}} as any,412 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);413414 // Try to create and nest a token in the wrong collection415 const newToken = await createItemExpectSuccess(bob, collection, 'NFT');416 await expect(executeTransaction(417 api, 418 bob, 419 api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1),420 ), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);421 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});422 });423 });424425 it('Admin (NFT): disallows a Token Owner to operate nesting when only Admin is allowed', async () => {426 await usingApi(async api => {427 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});428 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});429 await addToAllowListExpectSuccess(alice, collection, bob.address);430 await enableAllowListExpectSuccess(alice, collection);431 await enablePublicMintingExpectSuccess(alice, collection);432 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');433434 // Try to create a nested token as collection admin when it's disallowed435 await expect(executeTransaction(api, bob, api.tx.unique.createItem(436 collection,437 {Ethereum: tokenIdToAddress(collection, targetToken)},438 {nft: {}} as any,439 )), 'while creating nested token').to.be.rejectedWith(/common\.AddressNotInAllowlist/); 440441 // Try to create and nest a token in the wrong collection442 const newToken = await createItemExpectSuccess(bob, collection, 'NFT');443 await expect(executeTransaction(444 api, 445 bob, 446 api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1),447 ), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);448 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});449 });450 });451452 it('Admin (NFT): disallows an Admin to nest and unnest someone else\'s token', async () => {453 await usingApi(async api => {454 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});455 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});456457 await addToAllowListExpectSuccess(alice, collection, bob.address);458 await enableAllowListExpectSuccess(alice, collection);459 await enablePublicMintingExpectSuccess(alice, collection);460461 // Create a token to attempt to be nested into462 const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');463 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()};464465 // Try to nest somebody else's token466 const newToken = await createItemExpectSuccess(bob, collection, 'NFT');467 await expect(executeTransaction(468 api, 469 alice, 470 api.tx.unique.transfer(targetAddress, collection, newToken, 1),471 ), 'while nesting another\'s token token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);472 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});473474 // Nest a token as admin and try to unnest it, now belonging to someone else475 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);476 await expect(executeTransaction(477 api, 478 alice, 479 api.tx.unique.transferFrom(targetAddress, normalizeAccountId(alice), collection, nestedToken, 1),480 ), 'while unnesting another\'s token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);481 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal(targetAddress);482 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: bob.address});483 });484 });485486 it('Admin (NFT): disallows an Admin to nest a token from an unlisted collection (Restricted nesting)', async () => {487 await usingApi(async api => {488 const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});489 const collectionB = await createCollectionExpectSuccess({mode: {type: 'NFT'}});490 await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {collectionAdmin: true, restricted:[collectionA]}});491492 // Create a token to attempt to be nested into493 const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT');494495 // Try to create and nest a token in the wrong collection496 const newToken = await createItemExpectSuccess(alice, collectionB, 'NFT');497 await expect(executeTransaction(498 api, 499 alice, 500 api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionA, targetToken)}, collectionB, newToken, 1),501 ), 'while nesting a foreign token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);502 expect(await getTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Substrate: alice.address});503 });504 });505506 // ---------- Non-Fungible ----------507508 it('NFT: disallows to nest token if nesting is disabled', async () => {509 await usingApi(async api => {510 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});511 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {}});512 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');513514 // Try to create a nested token515 await expect(executeTransaction(api, alice, api.tx.unique.createItem(516 collection,517 {Ethereum: tokenIdToAddress(collection, targetToken)},518 {nft: {}} as any,519 )), 'while creating nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);520521 // Create a token to be nested522 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');523 // Try to nest524 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);525 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});526 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});527 });528 });529530 it('NFT: disallows a non-Owner to nest someone else\'s token', async () => {531 await usingApi(async api => {532 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});533 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});534535 await addToAllowListExpectSuccess(alice, collection, bob.address);536 await enableAllowListExpectSuccess(alice, collection);537 await enablePublicMintingExpectSuccess(alice, collection);538539 // Create a token to attempt to be nested into540 const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');541542 // Try to create a nested token in the wrong collection543 await expect(executeTransaction(api, alice, api.tx.unique.createItem(544 collection,545 {Ethereum: tokenIdToAddress(collection, targetToken)},546 {nft: {}} as any,547 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);548549 // Try to create and nest a token in the wrong collection550 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');551 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);552 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});553 });554 });555556 it('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {557 await usingApi(async api => {558 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});559 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[collection]}});560561 await addToAllowListExpectSuccess(alice, collection, bob.address);562 await enableAllowListExpectSuccess(alice, collection);563 await enablePublicMintingExpectSuccess(alice, collection);564565 // Create a token to attempt to be nested into566 const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');567568 // Try to create a nested token in the wrong collection569 await expect(executeTransaction(api, alice, api.tx.unique.createItem(570 collection,571 {Ethereum: tokenIdToAddress(collection, targetToken)},572 {nft: {}} as any,573 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);574575 // Try to create and nest a token in the wrong collection576 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');577 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);578 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});579 });580 });581582 it('NFT: disallows to nest token in an unlisted collection', async () => {583 await usingApi(async api => {584 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});585 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[]}});586587 // Create a token to attempt to be nested into588 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');589590 // Try to create a nested token in the wrong collection591 await expect(executeTransaction(api, alice, api.tx.unique.createItem(592 collection,593 {Ethereum: tokenIdToAddress(collection, targetToken)},594 {nft: {}} as any,595 )), 'while creating nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);596597 // Try to create and nest a token in the wrong collection598 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');599 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);600 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});601 });602 });603604 // ---------- Fungible ----------605606 it('Fungible: disallows to nest token if nesting is disabled', async () => {607 await usingApi(async api => {608 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});609 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {}});610 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');611 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};612613 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});614615 // Try to create a nested token616 await expect(executeTransaction(api, alice, api.tx.unique.createItem(617 collectionFT,618 targetAddress,619 {Fungible: {Value: 10}},620 )), 'while creating nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);621622 // Create a token to be nested623 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');624 // Try to nest625 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);626627 // Create another token to be nested628 const newToken2 = await createItemExpectSuccess(alice, collectionFT, 'Fungible');629 // Try to nest inside a fungible token630 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionFT, newToken)}, collectionFT, newToken2, 1)), 'while nesting new token inside fungible').to.be.rejectedWith(/fungible\.FungibleDisallowsNesting/);631 });632 });633634 it('Fungible: disallows a non-Owner to nest someone else\'s token', async () => {635 await usingApi(async api => {636 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});637 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});638639 await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);640 await enableAllowListExpectSuccess(alice, collectionNFT);641 await enablePublicMintingExpectSuccess(alice, collectionNFT);642643 // Create a token to attempt to be nested into644 const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');645 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};646647 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});648649 // Try to create a nested token in the wrong collection650 await expect(executeTransaction(api, alice, api.tx.unique.createItem(651 collectionFT,652 targetAddress,653 {Fungible: {Value: 10}},654 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);655656 // Try to create and nest a token in the wrong collection657 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');658 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);659 });660 });661662 it('Fungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {663 await usingApi(async api => {664 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});665 await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);666 await enableAllowListExpectSuccess(alice, collectionNFT);667 await enablePublicMintingExpectSuccess(alice, collectionNFT);668669 // Create a token to attempt to be nested into670 const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');671 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};672673 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});674 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionFT]}});675676 // Try to create a nested token in the wrong collection677 await expect(executeTransaction(api, alice, api.tx.unique.createItem(678 collectionFT,679 targetAddress,680 {Fungible: {Value: 10}},681 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);682683 // Try to create and nest a token in the wrong collection684 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');685 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);686 });687 });688689 it('Fungible: disallows to nest token in an unlisted collection', async () => {690 await usingApi(async api => {691 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});692 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[]}});693694 // Create a token to attempt to be nested into695 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');696 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};697698 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});699700 // Try to create a nested token in the wrong collection701 await expect(executeTransaction(api, alice, api.tx.unique.createItem(702 collectionFT,703 targetAddress,704 {Fungible: {Value: 10}},705 )), 'while creating a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);706707 // Try to create and nest a token in the wrong collection708 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');709 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);710 });711 });712713 // ---------- Re-Fungible ----------714715 it('ReFungible: disallows to nest token if nesting is disabled', async () => {716 await usingApi(async api => {717 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});718 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {}});719 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');720 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};721722 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});723724 // Create a nested token725 await expect(executeTransaction(api, alice, api.tx.unique.createItem(726 collectionRFT,727 targetAddress,728 {ReFungible: {pieces: 100}},729 )), 'while creating a nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);730731 // Create a token to be nested732 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');733 // Try to nest734 await transferExpectFailure(collectionRFT, newToken, alice, targetAddress, 100);735 // Try to nest736 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);737738 // Create another token to be nested739 const newToken2 = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');740 // Try to nest inside a fungible token741 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionRFT, newToken)}, collectionRFT, newToken2, 1)), 'while nesting new token inside refungible').to.be.rejectedWith(/refungible\.RefungibleDisallowsNesting/);742 });743 });744745 it('ReFungible: disallows a non-Owner to nest someone else\'s token', async () => {746 await usingApi(async api => {747 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});748 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});749750 await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);751 await enableAllowListExpectSuccess(alice, collectionNFT);752 await enablePublicMintingExpectSuccess(alice, collectionNFT);753754 // Create a token to attempt to be nested into755 const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');756 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};757758 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});759760 // Try to create a nested token in the wrong collection761 await expect(executeTransaction(api, alice, api.tx.unique.createItem(762 collectionRFT,763 targetAddress,764 {ReFungible: {pieces: 100}},765 )), 'while creating a nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);766767 // Try to create and nest a token in the wrong collection768 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');769 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);770 });771 });772773 it('ReFungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {774 await usingApi(async api => {775 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});776 await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);777 await enableAllowListExpectSuccess(alice, collectionNFT);778 await enablePublicMintingExpectSuccess(alice, collectionNFT);779780 // Create a token to attempt to be nested into781 const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');782 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};783784 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});785 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionRFT]}});786787 // Try to create a nested token in the wrong collection788 await expect(executeTransaction(api, alice, api.tx.unique.createItem(789 collectionRFT,790 targetAddress,791 {ReFungible: {pieces: 100}},792 )), 'while creating a nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);793794 // Try to create and nest a token in the wrong collection795 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');796 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);797 });798 });799800 it('ReFungible: disallows to nest token to an unlisted collection', async () => {801 await usingApi(async api => {802 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});803 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[]}});804805 // Create a token to attempt to be nested into806 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');807 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};808809 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});810811 // Try to create a nested token in the wrong collection812 await expect(executeTransaction(api, alice, api.tx.unique.createItem(813 collectionRFT,814 targetAddress,815 {ReFungible: {pieces: 100}},816 )), 'while creating a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);817818 // Try to create and nest a token in the wrong collection819 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');820 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);821 });822 });823});1import {expect} from 'chai';2import {tokenIdToAddress} from '../eth/util/helpers';3import usingApi, {executeTransaction} from '../substrate/substrate-api';4import {5 addCollectionAdminExpectSuccess,6 addToAllowListExpectSuccess,7 createCollectionExpectSuccess,8 createItemExpectSuccess,9 enableAllowListExpectSuccess,10 enablePublicMintingExpectSuccess,11 getTokenChildren,12 getTokenOwner,13 getTopmostTokenOwner,14 normalizeAccountId,15 setCollectionPermissionsExpectSuccess,16 transferExpectFailure,17 transferExpectSuccess,18 transferFromExpectSuccess,19 setCollectionLimitsExpectSuccess,20} from '../util/helpers';21import {IKeyringPair} from '@polkadot/types/types';2223let alice: IKeyringPair;24let bob: IKeyringPair;25let charlie: IKeyringPair;2627describe('Integration Test: Composite nesting tests', () => {28 before(async () => {29 await usingApi(async (_, privateKeyWrapper) => {30 alice = privateKeyWrapper('//Alice');31 bob = privateKeyWrapper('//Bob');32 });33 });3435 it('Performs the full suite: bundles a token, transfers, and unnests', async () => {36 await usingApi(async api => {37 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});38 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});39 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');4041 // Create a nested token42 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});43 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});44 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});4546 // Create a token to be nested47 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');4849 // Nest50 await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});51 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});52 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});5354 // Move bundle to different user55 await transferExpectSuccess(collection, targetToken, alice, {Substrate: bob.address});56 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: bob.address});57 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});5859 // Unnest60 await transferFromExpectSuccess(collection, newToken, bob, {Ethereum: tokenIdToAddress(collection, targetToken)}, {Substrate: bob.address});61 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});62 });63 });6465 it('Transfers an already bundled token', async () => {66 await usingApi(async api => {67 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});68 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});6970 const tokenA = await createItemExpectSuccess(alice, collection, 'NFT');71 const tokenB = await createItemExpectSuccess(alice, collection, 'NFT');7273 // Create a nested token74 const tokenC = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, tokenA)});75 expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});76 expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenA).toLowerCase()});7778 // Transfer the nested token to another token79 await expect(executeTransaction(80 api,81 alice,82 api.tx.unique.transferFrom(83 normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenA)}),84 normalizeAccountId({Ethereum: tokenIdToAddress(collection, tokenB)}),85 collection,86 tokenC,87 1,88 ),89 )).to.not.be.rejected;90 expect(await getTopmostTokenOwner(api, collection, tokenC)).to.be.deep.equal({Substrate: alice.address});91 expect(await getTokenOwner(api, collection, tokenC)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, tokenB).toLowerCase()});92 });93 });9495 it('Checks token children', async () => {96 await usingApi(async api => {97 const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});98 await setCollectionLimitsExpectSuccess(alice, collectionA, {ownerCanTransfer: true});99 await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {tokenOwner: true}});100 const collectionB = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});101102 const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT');103 const targetAddress = {Ethereum: tokenIdToAddress(collectionA, targetToken)};104 let children = await getTokenChildren(api, collectionA, targetToken);105 expect(children.length).to.be.equal(0, 'Children length check at creation');106107 // Create a nested NFT token108 const tokenA = await createItemExpectSuccess(alice, collectionA, 'NFT', targetAddress);109 children = await getTokenChildren(api, collectionA, targetToken);110 expect(children.length).to.be.equal(1, 'Children length check at nesting #1');111 expect(children).to.have.deep.members([112 {token: tokenA, collection: collectionA},113 ], 'Children contents check at nesting #1');114115 // Create then nest116 const tokenB = await createItemExpectSuccess(alice, collectionA, 'NFT');117 await transferExpectSuccess(collectionA, tokenB, alice, targetAddress);118 children = await getTokenChildren(api, collectionA, targetToken);119 expect(children.length).to.be.equal(2, 'Children length check at nesting #2');120 expect(children).to.have.deep.members([121 {token: tokenA, collection: collectionA},122 {token: tokenB, collection: collectionA},123 ], 'Children contents check at nesting #2');124125 // Move token B to a different user outside the nesting tree126 await transferExpectSuccess(collectionA, tokenB, alice, bob);127 children = await getTokenChildren(api, collectionA, targetToken);128 expect(children.length).to.be.equal(1, 'Children length check at unnesting');129 expect(children).to.be.have.deep.members([130 {token: tokenA, collection: collectionA},131 ], 'Children contents check at unnesting');132133 // Create a fungible token in another collection and then nest134 const tokenC = await createItemExpectSuccess(alice, collectionB, 'Fungible');135 await transferExpectSuccess(collectionB, tokenC, alice, targetAddress, 1, 'Fungible');136 children = await getTokenChildren(api, collectionA, targetToken);137 expect(children.length).to.be.equal(2, 'Children length check at nesting #3 (from another collection)');138 expect(children).to.be.have.deep.members([139 {token: tokenA, collection: collectionA},140 {token: tokenC, collection: collectionB},141 ], 'Children contents check at nesting #3 (from another collection)');142143 // Move the fungible token inside token A deeper in the nesting tree144 await transferFromExpectSuccess(collectionB, tokenC, alice, targetAddress, {Ethereum: tokenIdToAddress(collectionA, tokenA)}, 1, 'Fungible');145 children = await getTokenChildren(api, collectionA, targetToken);146 expect(children.length).to.be.equal(1, 'Children length check at deeper nesting');147 expect(children).to.be.have.deep.members([148 {token: tokenA, collection: collectionA},149 ], 'Children contents check at deeper nesting');150 });151 });152});153154describe('Integration Test: Various token type nesting', async () => {155 before(async () => {156 await usingApi(async (_, privateKeyWrapper) => {157 alice = privateKeyWrapper('//Alice');158 bob = privateKeyWrapper('//Bob');159 charlie = privateKeyWrapper('//Charlie');160 });161 });162163 it('Admin (NFT): allows an Admin to nest a token', async () => {164 await usingApi(async api => {165 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});166 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});167 await addCollectionAdminExpectSuccess(alice, collection, bob.address);168 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);169170 // Create a nested token171 const nestedToken = await createItemExpectSuccess(bob, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});172 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: charlie.address});173 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});174175 // Create a token to be nested and nest176 const newToken = await createItemExpectSuccess(bob, collection, 'NFT');177 await transferExpectSuccess(collection, newToken, bob, {Ethereum: tokenIdToAddress(collection, targetToken)});178 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: charlie.address});179 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});180 });181 });182183 it('Admin (NFT): Admin and Token Owner can operate together', async () => {184 await usingApi(async api => {185 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});186 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, collectionAdmin: true}});187 await addCollectionAdminExpectSuccess(alice, collection, bob.address);188 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);189190 // Create a nested token by an administrator191 const nestedToken = await createItemExpectSuccess(bob, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});192 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: charlie.address});193 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});194195 // Create a token and allow the owner to nest too196 const newToken = await createItemExpectSuccess(alice, collection, 'NFT', charlie.address);197 await transferExpectSuccess(collection, newToken, charlie, {Ethereum: tokenIdToAddress(collection, nestedToken)});198 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: charlie.address});199 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, nestedToken).toLowerCase()});200 });201 });202203 it('Admin (NFT): allows an Admin to nest a token (Restricted nesting)', async () => {204 await usingApi(async api => {205 const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});206 await addCollectionAdminExpectSuccess(alice, collectionA, bob.address);207 const collectionB = await createCollectionExpectSuccess({mode: {type: 'NFT'}});208 await addCollectionAdminExpectSuccess(alice, collectionB, bob.address);209 await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {collectionAdmin: true, restricted:[collectionA, collectionB]}});210 const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT', charlie.address);211212 // Create a nested token213 const nestedToken = await createItemExpectSuccess(bob, collectionB, 'NFT', {Ethereum: tokenIdToAddress(collectionA, targetToken)});214 expect(await getTopmostTokenOwner(api, collectionB, nestedToken)).to.be.deep.equal({Substrate: charlie.address});215 expect(await getTokenOwner(api, collectionB, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collectionA, targetToken).toLowerCase()});216217 // Create a token to be nested and nest218 const newToken = await createItemExpectSuccess(bob, collectionB, 'NFT');219 await transferExpectSuccess(collectionB, newToken, bob, {Ethereum: tokenIdToAddress(collectionA, targetToken)});220 expect(await getTopmostTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Substrate: charlie.address});221 expect(await getTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collectionA, targetToken).toLowerCase()});222 });223 });224225 // ---------- Non-Fungible ----------226227 it('NFT: allows an Owner to nest/unnest their token', async () => {228 await usingApi(async api => {229 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});230 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});231 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');232233 // Create a nested token234 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});235 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});236 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});237238 // Create a token to be nested and nest239 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');240 await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});241 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});242 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});243 });244 });245246 it('NFT: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {247 await usingApi(async api => {248 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});249 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[collection]}});250 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');251252 // Create a nested token253 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, targetToken)});254 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: alice.address});255 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});256257 // Create a token to be nested and nest258 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');259 await transferExpectSuccess(collection, newToken, alice, {Ethereum: tokenIdToAddress(collection, targetToken)});260 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});261 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()});262 });263 });264265 // ---------- Fungible ----------266267 it('Fungible: allows an Owner to nest/unnest their token', async () => {268 await usingApi(async api => {269 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});270 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});271 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});272 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};273274 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});275276 // Create a nested token277 await expect(executeTransaction(api, alice, api.tx.unique.createItem(278 collectionFT,279 targetAddress,280 {Fungible: {Value: 10}},281 ))).to.not.be.rejected;282283 // Nest a new token284 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');285 await transferExpectSuccess(collectionFT, newToken, alice, targetAddress, 1, 'Fungible');286 });287 });288289 it('Fungible: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {290 await usingApi(async api => {291 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});292 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});293 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};294295 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});296297 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted: [collectionFT]}});298299 // Create a nested token300 await expect(executeTransaction(api, alice, api.tx.unique.createItem(301 collectionFT,302 targetAddress,303 {Fungible: {Value: 10}},304 ))).to.not.be.rejected;305306 // Nest a new token307 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');308 await transferExpectSuccess(collectionFT, newToken, alice, targetAddress, 1, 'Fungible');309 });310 });311312 // ---------- Re-Fungible ----------313314 it('ReFungible: allows an Owner to nest/unnest their token', async () => {315 await usingApi(async api => {316 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});317 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});318 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});319 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};320321 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});322323 // Create a nested token324 await expect(executeTransaction(api, alice, api.tx.unique.createItem(325 collectionRFT,326 targetAddress,327 {ReFungible: {pieces: 100}},328 ))).to.not.be.rejected;329330 // Nest a new token331 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');332 await transferExpectSuccess(collectionRFT, newToken, alice, targetAddress, 100, 'ReFungible');333 });334 });335336 it('ReFungible: allows an Owner to nest/unnest their token (Restricted nesting)', async () => {337 await usingApi(async api => {338 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});339 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT', {Substrate: alice.address});340 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};341342 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});343344 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionRFT]}});345346 // Create a nested token347 await expect(executeTransaction(api, alice, api.tx.unique.createItem(348 collectionRFT,349 targetAddress,350 {ReFungible: {pieces: 100}},351 ))).to.not.be.rejected;352353 // Nest a new token354 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');355 await transferExpectSuccess(collectionRFT, newToken, alice, targetAddress, 100, 'ReFungible');356 });357 });358});359360describe('Negative Test: Nesting', async() => {361 before(async () => {362 await usingApi(async (_, privateKeyWrapper) => {363 alice = privateKeyWrapper('//Alice');364 bob = privateKeyWrapper('//Bob');365 });366 });367368 it('Disallows excessive token nesting', async () => {369 await usingApi(async api => {370 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});371 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});372 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');373374 const maxNestingLevel = 5;375 let prevToken = targetToken;376377 // Create a nested-token matryoshka378 for (let i = 0; i < maxNestingLevel; i++) {379 const nestedToken = await createItemExpectSuccess(380 alice,381 collection,382 'NFT',383 {Ethereum: tokenIdToAddress(collection, prevToken)},384 );385386 prevToken = nestedToken;387 }388389 // The nesting depth is limited by `maxNestingLevel`390 await expect(executeTransaction(api, alice, api.tx.unique.createItem(391 collection,392 {Ethereum: tokenIdToAddress(collection, prevToken)},393 {nft: {}} as any,394 )), 'while creating nested token').to.be.rejectedWith(/^structure\.DepthLimit$/);395396 expect(await getTopmostTokenOwner(api, collection, prevToken)).to.be.deep.equal({Substrate: alice.address});397 });398 });399400 // ---------- Admin ------------401402 it('Admin (NFT): disallows an Admin to operate nesting when only TokenOwner is allowed', async () => {403 await usingApi(async api => {404 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});405 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});406 await addCollectionAdminExpectSuccess(alice, collection, bob.address);407 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');408409 // Try to create a nested token as collection admin when it's disallowed410 await expect(executeTransaction(api, bob, api.tx.unique.createItem(411 collection,412 {Ethereum: tokenIdToAddress(collection, targetToken)},413 {nft: {}} as any,414 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);415416 // Try to create and nest a token in the wrong collection417 const newToken = await createItemExpectSuccess(bob, collection, 'NFT');418 await expect(executeTransaction(419 api, 420 bob, 421 api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1),422 ), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);423 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});424 });425 });426427 it('Admin (NFT): disallows a Token Owner to operate nesting when only Admin is allowed', async () => {428 await usingApi(async api => {429 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});430 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});431 await addToAllowListExpectSuccess(alice, collection, bob.address);432 await enableAllowListExpectSuccess(alice, collection);433 await enablePublicMintingExpectSuccess(alice, collection);434 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');435436 // Try to create a nested token as collection admin when it's disallowed437 await expect(executeTransaction(api, bob, api.tx.unique.createItem(438 collection,439 {Ethereum: tokenIdToAddress(collection, targetToken)},440 {nft: {}} as any,441 )), 'while creating nested token').to.be.rejectedWith(/common\.AddressNotInAllowlist/); 442443 // Try to create and nest a token in the wrong collection444 const newToken = await createItemExpectSuccess(bob, collection, 'NFT');445 await expect(executeTransaction(446 api, 447 bob, 448 api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1),449 ), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);450 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});451 });452 });453454 it('Admin (NFT): disallows an Admin to nest and unnest someone else\'s token', async () => {455 await usingApi(async api => {456 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});457 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {collectionAdmin: true}});458459 await addToAllowListExpectSuccess(alice, collection, bob.address);460 await enableAllowListExpectSuccess(alice, collection);461 await enablePublicMintingExpectSuccess(alice, collection);462463 // Create a token to attempt to be nested into464 const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');465 const targetAddress = {Ethereum: tokenIdToAddress(collection, targetToken).toLowerCase()};466467 // Try to nest somebody else's token468 const newToken = await createItemExpectSuccess(bob, collection, 'NFT');469 await expect(executeTransaction(470 api, 471 alice, 472 api.tx.unique.transfer(targetAddress, collection, newToken, 1),473 ), 'while nesting another\'s token token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);474 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address});475476 // Nest a token as admin and try to unnest it, now belonging to someone else477 const nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', targetAddress);478 await expect(executeTransaction(479 api, 480 alice, 481 api.tx.unique.transferFrom(targetAddress, normalizeAccountId(alice), collection, nestedToken, 1),482 ), 'while unnesting another\'s token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);483 expect(await getTokenOwner(api, collection, nestedToken)).to.be.deep.equal(targetAddress);484 expect(await getTopmostTokenOwner(api, collection, nestedToken)).to.be.deep.equal({Substrate: bob.address});485 });486 });487488 it('Admin (NFT): disallows an Admin to nest a token from an unlisted collection (Restricted nesting)', async () => {489 await usingApi(async api => {490 const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});491 const collectionB = await createCollectionExpectSuccess({mode: {type: 'NFT'}});492 await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {collectionAdmin: true, restricted:[collectionA]}});493494 // Create a token to attempt to be nested into495 const targetToken = await createItemExpectSuccess(alice, collectionA, 'NFT');496497 // Try to create and nest a token in the wrong collection498 const newToken = await createItemExpectSuccess(alice, collectionB, 'NFT');499 await expect(executeTransaction(500 api, 501 alice, 502 api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionA, targetToken)}, collectionB, newToken, 1),503 ), 'while nesting a foreign token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);504 expect(await getTokenOwner(api, collectionB, newToken)).to.be.deep.equal({Substrate: alice.address});505 });506 });507508 // ---------- Non-Fungible ----------509510 it('NFT: disallows to nest token if nesting is disabled', async () => {511 await usingApi(async api => {512 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});513 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {}});514 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');515516 // Try to create a nested token517 await expect(executeTransaction(api, alice, api.tx.unique.createItem(518 collection,519 {Ethereum: tokenIdToAddress(collection, targetToken)},520 {nft: {}} as any,521 )), 'while creating nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);522523 // Create a token to be nested524 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');525 // Try to nest526 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);527 expect(await getTopmostTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});528 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});529 });530 });531532 it('NFT: disallows a non-Owner to nest someone else\'s token', async () => {533 await usingApi(async api => {534 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});535 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}});536537 await addToAllowListExpectSuccess(alice, collection, bob.address);538 await enableAllowListExpectSuccess(alice, collection);539 await enablePublicMintingExpectSuccess(alice, collection);540541 // Create a token to attempt to be nested into542 const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');543544 // Try to create a nested token in the wrong collection545 await expect(executeTransaction(api, alice, api.tx.unique.createItem(546 collection,547 {Ethereum: tokenIdToAddress(collection, targetToken)},548 {nft: {}} as any,549 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);550551 // Try to create and nest a token in the wrong collection552 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');553 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);554 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});555 });556 });557558 it('NFT: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {559 await usingApi(async api => {560 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});561 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[collection]}});562563 await addToAllowListExpectSuccess(alice, collection, bob.address);564 await enableAllowListExpectSuccess(alice, collection);565 await enablePublicMintingExpectSuccess(alice, collection);566567 // Create a token to attempt to be nested into568 const targetToken = await createItemExpectSuccess(bob, collection, 'NFT');569570 // Try to create a nested token in the wrong collection571 await expect(executeTransaction(api, alice, api.tx.unique.createItem(572 collection,573 {Ethereum: tokenIdToAddress(collection, targetToken)},574 {nft: {}} as any,575 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);576577 // Try to create and nest a token in the wrong collection578 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');579 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.AddressNotInAllowlist/);580 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});581 });582 });583584 it('NFT: disallows to nest token in an unlisted collection', async () => {585 await usingApi(async api => {586 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});587 await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true, restricted:[]}});588589 // Create a token to attempt to be nested into590 const targetToken = await createItemExpectSuccess(alice, collection, 'NFT');591592 // Try to create a nested token in the wrong collection593 await expect(executeTransaction(api, alice, api.tx.unique.createItem(594 collection,595 {Ethereum: tokenIdToAddress(collection, targetToken)},596 {nft: {}} as any,597 )), 'while creating nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);598599 // Try to create and nest a token in the wrong collection600 const newToken = await createItemExpectSuccess(alice, collection, 'NFT');601 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collection, targetToken)}, collection, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);602 expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: alice.address});603 });604 });605606 // ---------- Fungible ----------607608 it('Fungible: disallows to nest token if nesting is disabled', async () => {609 await usingApi(async api => {610 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});611 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {}});612 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');613 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};614615 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});616617 // Try to create a nested token618 await expect(executeTransaction(api, alice, api.tx.unique.createItem(619 collectionFT,620 targetAddress,621 {Fungible: {Value: 10}},622 )), 'while creating nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);623624 // Create a token to be nested625 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');626 // Try to nest627 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);628629 // Create another token to be nested630 const newToken2 = await createItemExpectSuccess(alice, collectionFT, 'Fungible');631 // Try to nest inside a fungible token632 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionFT, newToken)}, collectionFT, newToken2, 1)), 'while nesting new token inside fungible').to.be.rejectedWith(/fungible\.FungibleDisallowsNesting/);633 });634 });635636 it('Fungible: disallows a non-Owner to nest someone else\'s token', async () => {637 await usingApi(async api => {638 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});639 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});640641 await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);642 await enableAllowListExpectSuccess(alice, collectionNFT);643 await enablePublicMintingExpectSuccess(alice, collectionNFT);644645 // Create a token to attempt to be nested into646 const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');647 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};648649 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});650651 // Try to create a nested token in the wrong collection652 await expect(executeTransaction(api, alice, api.tx.unique.createItem(653 collectionFT,654 targetAddress,655 {Fungible: {Value: 10}},656 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);657658 // Try to create and nest a token in the wrong collection659 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');660 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);661 });662 });663664 it('Fungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {665 await usingApi(async api => {666 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});667 await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);668 await enableAllowListExpectSuccess(alice, collectionNFT);669 await enablePublicMintingExpectSuccess(alice, collectionNFT);670671 // Create a token to attempt to be nested into672 const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');673 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};674675 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});676 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionFT]}});677678 // Try to create a nested token in the wrong collection679 await expect(executeTransaction(api, alice, api.tx.unique.createItem(680 collectionFT,681 targetAddress,682 {Fungible: {Value: 10}},683 )), 'while creating nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);684685 // Try to create and nest a token in the wrong collection686 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');687 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);688 });689 });690691 it('Fungible: disallows to nest token in an unlisted collection', async () => {692 await usingApi(async api => {693 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});694 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[]}});695696 // Create a token to attempt to be nested into697 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');698 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};699700 const collectionFT = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});701702 // Try to create a nested token in the wrong collection703 await expect(executeTransaction(api, alice, api.tx.unique.createItem(704 collectionFT,705 targetAddress,706 {Fungible: {Value: 10}},707 )), 'while creating a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);708709 // Try to create and nest a token in the wrong collection710 const newToken = await createItemExpectSuccess(alice, collectionFT, 'Fungible');711 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);712 });713 });714715 // ---------- Re-Fungible ----------716717 it('ReFungible: disallows to nest token if nesting is disabled', async () => {718 await usingApi(async api => {719 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});720 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {}});721 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');722 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};723724 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});725726 // Create a nested token727 await expect(executeTransaction(api, alice, api.tx.unique.createItem(728 collectionRFT,729 targetAddress,730 {ReFungible: {pieces: 100}},731 )), 'while creating a nested token').to.be.rejectedWith(/^common\.UserIsNotAllowedToNest$/);732733 // Create a token to be nested734 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');735 // Try to nest736 await transferExpectFailure(collectionRFT, newToken, alice, targetAddress, 100);737 // Try to nest738 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);739740 // Create another token to be nested741 const newToken2 = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');742 // Try to nest inside a fungible token743 await expect(executeTransaction(api, alice, api.tx.unique.transfer({Ethereum: tokenIdToAddress(collectionRFT, newToken)}, collectionRFT, newToken2, 1)), 'while nesting new token inside refungible').to.be.rejectedWith(/refungible\.RefungibleDisallowsNesting/);744 });745 });746747 it('ReFungible: disallows a non-Owner to nest someone else\'s token', async () => {748 await usingApi(async api => {749 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});750 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true}});751752 await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);753 await enableAllowListExpectSuccess(alice, collectionNFT);754 await enablePublicMintingExpectSuccess(alice, collectionNFT);755756 // Create a token to attempt to be nested into757 const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');758 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};759760 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});761762 // Try to create a nested token in the wrong collection763 await expect(executeTransaction(api, alice, api.tx.unique.createItem(764 collectionRFT,765 targetAddress,766 {ReFungible: {pieces: 100}},767 )), 'while creating a nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);768769 // Try to create and nest a token in the wrong collection770 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');771 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);772 });773 });774775 it('ReFungible: disallows a non-Owner to nest someone else\'s token (Restricted nesting)', async () => {776 await usingApi(async api => {777 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});778 await addToAllowListExpectSuccess(alice, collectionNFT, bob.address);779 await enableAllowListExpectSuccess(alice, collectionNFT);780 await enablePublicMintingExpectSuccess(alice, collectionNFT);781782 // Create a token to attempt to be nested into783 const targetToken = await createItemExpectSuccess(bob, collectionNFT, 'NFT');784 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};785786 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});787 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[collectionRFT]}});788789 // Try to create a nested token in the wrong collection790 await expect(executeTransaction(api, alice, api.tx.unique.createItem(791 collectionRFT,792 targetAddress,793 {ReFungible: {pieces: 100}},794 )), 'while creating a nested token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);795796 // Try to create and nest a token in the wrong collection797 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');798 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.UserIsNotAllowedToNest/);799 });800 });801802 it('ReFungible: disallows to nest token to an unlisted collection', async () => {803 await usingApi(async api => {804 const collectionNFT = await createCollectionExpectSuccess({mode: {type: 'NFT'}});805 await setCollectionPermissionsExpectSuccess(alice, collectionNFT, {nesting: {tokenOwner: true, restricted:[]}});806807 // Create a token to attempt to be nested into808 const targetToken = await createItemExpectSuccess(alice, collectionNFT, 'NFT');809 const targetAddress = {Ethereum: tokenIdToAddress(collectionNFT, targetToken)};810811 const collectionRFT = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});812813 // Try to create a nested token in the wrong collection814 await expect(executeTransaction(api, alice, api.tx.unique.createItem(815 collectionRFT,816 targetAddress,817 {ReFungible: {pieces: 100}},818 )), 'while creating a nested token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);819820 // Try to create and nest a token in the wrong collection821 const newToken = await createItemExpectSuccess(alice, collectionRFT, 'ReFungible');822 await expect(executeTransaction(api, alice, api.tx.unique.transfer(targetAddress, collectionRFT, newToken, 1)), 'while nesting new token').to.be.rejectedWith(/common\.SourceCollectionIsNotAllowedToNest/);823 });824 });825});tests/src/transferFrom.test.tsdiffbeforeafterboth--- a/tests/src/transferFrom.test.ts
+++ b/tests/src/transferFrom.test.ts
@@ -99,6 +99,7 @@
it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {
const collectionId = await createCollectionExpectSuccess();
+ await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});
const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);
await transferFromExpectSuccess(collectionId, itemId, alice, bob, charlie);
@@ -257,6 +258,7 @@
await usingApi(async () => {
// nft
const nftCollectionId = await createCollectionExpectSuccess();
+ await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {ownerCanTransfer: true});
const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');
await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);
await approveExpectFail(nftCollectionId, newNftTokenId, alice, bob);
@@ -266,6 +268,7 @@
it('transferFrom burnt token before approve Fungible', async () => {
await usingApi(async () => {
const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
+ await setCollectionLimitsExpectSuccess(alice, fungibleCollectionId, {ownerCanTransfer: true});
const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');
await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);
await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);
@@ -276,6 +279,7 @@
it('transferFrom burnt token before approve ReFungible', async () => {
await usingApi(async () => {
const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
+ await setCollectionLimitsExpectSuccess(alice, reFungibleCollectionId, {ownerCanTransfer: true});
const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');
await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);
await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, alice, bob);
tests/src/util/helpers.tsdiffbeforeafterboth--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -921,6 +921,18 @@
});
}
+export async function adminApproveFromExpectFail(
+ collectionId: number,
+ tokenId: number, admin: IKeyringPair, owner: CrossAccountId | string, approved: CrossAccountId | string, amount: number | bigint = 1,
+) {
+ await usingApi(async (api: ApiPromise) => {
+ const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved), collectionId, tokenId, amount);
+ const events = await expect(submitTransactionAsync(admin, approveUniqueTx)).to.be.rejected;
+ const result = getGenericResult(events);
+ expect(result.success).to.be.false;
+ });
+}
+
export async function
getFreeBalance(account: IKeyringPair): Promise<bigint> {
let balance = 0n;