git.delta.rocks / unique-network / refs/commits / 670ea143e393

difftreelog

fix rft admin can unnest

Daniel Shiposha2023-01-13parent: #5c9ffb7.patch.diff
in: master

2 files changed

modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
1178 collection.check_allowlist(spender)?;1178 collection.check_allowlist(spender)?;
1179 }1179 }
1180
1181 if collection.limits.owner_can_transfer() && collection.is_owner_or_admin(spender) {
1182 return Ok(None);
1183 }
1184
1180 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {1185 if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {
1181 // TODO: should collection owner be allowed to perform this transfer?1186 // TODO: should collection owner be allowed to perform this transfer?
modifiedtests/src/nesting/nest.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/nest.test.ts
+++ b/tests/src/nesting/nest.test.ts
@@ -16,6 +16,7 @@
 
 import {IKeyringPair} from '@polkadot/types/types';
 import {expect, itSub, Pallets, usingPlaygrounds} from '../util';
+import {UniqueNFToken, UniqueRFToken} from '../util/playgrounds/unique';
 
 describe('Integration Test: Composite nesting tests', () => {
   let alice: IKeyringPair;
@@ -138,7 +139,7 @@
   before(async () => {
     await usingPlaygrounds(async (helper, privateKey) => {
       const donor = await privateKey({filename: __filename});
-      [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor);
+      [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 20n, 20n], donor);
     });
   });
 
@@ -335,6 +336,205 @@
     await newToken.transfer(charlie, targetToken.nestingAccount(), 2n);
     expect(await newToken.getBalance(targetToken.nestingAccount())).to.be.equal(2n);
   });
+
+  async function checkNestedRft({
+    expectedBalance,
+    childrenShouldPresent,
+    nestedRft,
+    targetNft,
+  }: {
+    expectedBalance: bigint,
+    childrenShouldPresent: boolean,
+    nestedRft: UniqueRFToken,
+    targetNft: UniqueNFToken,
+  }) {
+    const balance = await nestedRft.getBalance(targetNft.nestingAccount());
+    expect(balance).to.be.equal(expectedBalance);
+
+    const children = await targetNft.getChildren();
+
+    if (childrenShouldPresent) {
+      expect(children[0]).to.be.deep.equal({
+        collectionId: nestedRft.collectionId,
+        tokenId: nestedRft.tokenId,
+      });
+    } else {
+      expect(children.length).to.be.equal(0);
+    }
+  }
+
+  itSub.ifWithPallets('ReFungible: allows a collection owner to transfer nested token', [Pallets.ReFungible], async ({helper}) => {
+    const collectionNFT = await helper.nft.mintCollection(alice);
+    const collectionRFT = await helper.rft.mintCollection(alice, {
+      limits: {
+        ownerCanTransfer: true,
+      },
+    });
+
+    await collectionNFT.setPermissions(alice, {nesting: {tokenOwner: true}});
+
+    const targetNft = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
+    const nestedRft = await collectionRFT.mintToken(alice, 5n, {Substrate: charlie.address});
+
+    await nestedRft.transfer(charlie, targetNft.nestingAccount(), 5n);
+    await checkNestedRft({
+      expectedBalance: 5n,
+      childrenShouldPresent: true,
+      nestedRft,
+      targetNft,
+    });
+
+    await nestedRft.transferFrom(alice, targetNft.nestingAccount(), {Substrate: bob.address}, 2n);
+    await checkNestedRft({
+      expectedBalance: 3n,
+      childrenShouldPresent: true,
+      nestedRft,
+      targetNft,
+    });
+    expect(await nestedRft.getBalance({Substrate: bob.address})).to.be.equal(2n);
+
+    await nestedRft.transferFrom(alice, targetNft.nestingAccount(), {Substrate: bob.address}, 3n);
+    await checkNestedRft({
+      expectedBalance: 0n,
+      childrenShouldPresent: false,
+      nestedRft,
+      targetNft,
+    });
+    expect(await nestedRft.getBalance({Substrate: bob.address})).to.be.equal(5n);
+  });
+
+  itSub.ifWithPallets('ReFungible: allows a collection admin to transfer nested token', [Pallets.ReFungible], async ({helper}) => {
+    const collectionNFT = await helper.nft.mintCollection(alice);
+    const collectionRFT = await helper.rft.mintCollection(alice, {
+      limits: {
+        ownerCanTransfer: true,
+      },
+    });
+    await collectionRFT.addAdmin(alice, {Substrate: bob.address});
+
+    await collectionNFT.setPermissions(alice, {nesting: {tokenOwner: true}});
+
+    const targetNft = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
+    const nestedRft = await collectionRFT.mintToken(alice, 5n, {Substrate: charlie.address});
+
+    await nestedRft.transfer(charlie, targetNft.nestingAccount(), 5n);
+    await checkNestedRft({
+      expectedBalance: 5n,
+      childrenShouldPresent: true,
+      nestedRft,
+      targetNft,
+    });
+
+    await nestedRft.transferFrom(bob, targetNft.nestingAccount(), {Substrate: bob.address}, 2n);
+    await checkNestedRft({
+      expectedBalance: 3n,
+      childrenShouldPresent: true,
+      nestedRft,
+      targetNft,
+    });
+    expect(await nestedRft.getBalance({Substrate: bob.address})).to.be.equal(2n);
+
+    await nestedRft.transferFrom(bob, targetNft.nestingAccount(), {Substrate: bob.address}, 3n);
+    await checkNestedRft({
+      expectedBalance: 0n,
+      childrenShouldPresent: false,
+      nestedRft,
+      targetNft,
+    });
+    expect(await nestedRft.getBalance({Substrate: bob.address})).to.be.equal(5n);
+  });
+
+  itSub.ifWithPallets('ReFungible: allows a collection owner to burn nested token', [Pallets.ReFungible], async ({helper}) => {
+    const collectionNFT = await helper.nft.mintCollection(alice, {
+      limits: {
+        ownerCanTransfer: true,
+      },
+    });
+    const collectionRFT = await helper.rft.mintCollection(alice, {
+      limits: {
+        ownerCanTransfer: true,
+      },
+    });
+
+    await collectionNFT.setPermissions(alice, {nesting: {tokenOwner: true}});
+
+    const targetNft = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
+    const nestedRft = await collectionRFT.mintToken(alice, 5n, {Substrate: charlie.address});
+
+    await nestedRft.transfer(charlie, targetNft.nestingAccount(), 5n);
+    await checkNestedRft({
+      expectedBalance: 5n,
+      childrenShouldPresent: true,
+      nestedRft,
+      targetNft,
+    });
+
+    await nestedRft.burnFrom(alice, targetNft.nestingAccount(), 2n);
+    await checkNestedRft({
+      expectedBalance: 3n,
+      childrenShouldPresent: true,
+      nestedRft,
+      targetNft,
+    });
+
+    await nestedRft.burnFrom(alice, targetNft.nestingAccount(), 3n);
+    await checkNestedRft({
+      expectedBalance: 0n,
+      childrenShouldPresent: false,
+      nestedRft,
+      targetNft,
+    });
+
+    // Check if we can burn target NFT when all nested RFTs are gone
+    await targetNft.burnFrom(alice, {Substrate: charlie.address});
+  });
+
+  itSub.ifWithPallets('ReFungible: allows a collection admin to burn nested token', [Pallets.ReFungible], async ({helper}) => {
+    const collectionNFT = await helper.nft.mintCollection(alice, {
+      limits: {
+        ownerCanTransfer: true,
+      },
+    });
+    const collectionRFT = await helper.rft.mintCollection(alice, {
+      limits: {
+        ownerCanTransfer: true,
+      },
+    });
+    await collectionNFT.addAdmin(alice, {Substrate: bob.address});
+    await collectionRFT.addAdmin(alice, {Substrate: bob.address});
+
+    await collectionNFT.setPermissions(alice, {nesting: {tokenOwner: true}});
+
+    const targetNft = await collectionNFT.mintToken(alice, {Substrate: charlie.address});
+    const nestedRft = await collectionRFT.mintToken(alice, 5n, {Substrate: charlie.address});
+
+    await nestedRft.transfer(charlie, targetNft.nestingAccount(), 5n);
+    await checkNestedRft({
+      expectedBalance: 5n,
+      childrenShouldPresent: true,
+      nestedRft,
+      targetNft,
+    });
+
+    await nestedRft.burnFrom(bob, targetNft.nestingAccount(), 2n);
+    await checkNestedRft({
+      expectedBalance: 3n,
+      childrenShouldPresent: true,
+      nestedRft,
+      targetNft,
+    });
+
+    await nestedRft.burnFrom(bob, targetNft.nestingAccount(), 3n);
+    await checkNestedRft({
+      expectedBalance: 0n,
+      childrenShouldPresent: false,
+      nestedRft,
+      targetNft,
+    });
+
+    // Check if we can burn target NFT when all nested RFTs are gone
+    await targetNft.burnFrom(bob, {Substrate: charlie.address});
+  });
 });
 
 describe('Negative Test: Nesting', () => {