git.delta.rocks / unique-network / refs/commits / 6b48a5a77b12

difftreelog

OwnerCanTransfer flag

Dev2022-06-14parent: #681a5b7.patch.diff
in: master

10 files changed

modifiedpallets/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,
 			),
modifiedprimitives/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)
modifiedtests/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);
   });
 });
 
modifiedtests/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);
 
modifiedtests/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});
modifiedtests/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;
       }
modifiedtests/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(
modifiedtests/src/nesting/nest.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/nest.test.ts
+++ b/tests/src/nesting/nest.test.ts
@@ -15,6 +15,7 @@
   transferExpectFailure,
   transferExpectSuccess,
   transferFromExpectSuccess,
+  setCollectionLimitsExpectSuccess,
 } from '../util/helpers';
 import {IKeyringPair} from '@polkadot/types/types';
 
@@ -92,6 +93,7 @@
   it('Checks token children', async () => {
     await usingApi(async api => {
       const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
+      await setCollectionLimitsExpectSuccess(alice, collectionA, {ownerCanTransfer: true});
       await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {tokenOwner: true}});
       const collectionB = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
 
modifiedtests/src/transferFrom.test.tsdiffbeforeafterboth
before · tests/src/transferFrom.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {ApiPromise} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import chai from 'chai';20import chaiAsPromised from 'chai-as-promised';21import {default as usingApi} from './substrate/substrate-api';22import {23  approveExpectFail,24  approveExpectSuccess,25  createCollectionExpectSuccess,26  createFungibleItemExpectSuccess,27  createItemExpectSuccess,28  getAllowance,29  transferFromExpectFail,30  transferFromExpectSuccess,31  burnItemExpectSuccess,32  setCollectionLimitsExpectSuccess,33  getCreatedCollectionCount,34} from './util/helpers';3536chai.use(chaiAsPromised);37const expect = chai.expect;3839describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {40  let alice: IKeyringPair;41  let bob: IKeyringPair;42  let charlie: IKeyringPair;4344  before(async () => {45    await usingApi(async (api, privateKeyWrapper) => {46      alice = privateKeyWrapper('//Alice');47      bob = privateKeyWrapper('//Bob');48      charlie = privateKeyWrapper('//Charlie');49    });50  });5152  it('Execute the extrinsic and check nftItemList - owner of token', async () => {53    await usingApi(async () => {54      // nft55      const nftCollectionId = await createCollectionExpectSuccess();56      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');57      await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address);5859      await transferFromExpectSuccess(nftCollectionId, newNftTokenId, bob, alice, charlie, 1, 'NFT');6061      // fungible62      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});63      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');64      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);65      await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1, 'Fungible');6667      // reFungible68      const reFungibleCollectionId = await69      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});70      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');71      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 100);72      await transferFromExpectSuccess(73        reFungibleCollectionId,74        newReFungibleTokenId,75        bob,76        alice,77        charlie,78        100,79        'ReFungible',80      );81    });82  });8384  it('Should reduce allowance if value is big', async () => {85    await usingApi(async (api, privateKeyWrapper) => {86      const alice = privateKeyWrapper('//Alice');87      const bob = privateKeyWrapper('//Bob');88      const charlie = privateKeyWrapper('//Charlie');8990      // fungible91      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});92      const newFungibleTokenId = await createFungibleItemExpectSuccess(alice, fungibleCollectionId, {Value: 500000n});9394      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 500000n);95      await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 500000n, 'Fungible');96      expect(await getAllowance(api, fungibleCollectionId, alice.address, bob.address, newFungibleTokenId)).to.equal(0n);97    });98  });99100  it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {101    const collectionId = await createCollectionExpectSuccess();102    const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);103104    await transferFromExpectSuccess(collectionId, itemId, alice, bob, charlie);105  });106});107108describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {109  let alice: IKeyringPair;110  let bob: IKeyringPair;111  let charlie: IKeyringPair;112113  before(async () => {114    await usingApi(async (api, privateKeyWrapper) => {115      alice = privateKeyWrapper('//Alice');116      bob = privateKeyWrapper('//Bob');117      charlie = privateKeyWrapper('//Charlie');118    });119  });120121  it('transferFrom for a collection that does not exist', async () => {122    await usingApi(async (api: ApiPromise) => {123      // nft124      const nftCollectionCount = await getCreatedCollectionCount(api);125      await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);126127      await transferFromExpectFail(nftCollectionCount + 1, 1, bob, alice, charlie, 1);128129      // fungible130      const fungibleCollectionCount = await getCreatedCollectionCount(api);131      await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);132133      await transferFromExpectFail(fungibleCollectionCount + 1, 0, bob, alice, charlie, 1);134      // reFungible135      const reFungibleCollectionCount = await getCreatedCollectionCount(api);136      await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);137138      await transferFromExpectFail(reFungibleCollectionCount + 1, 1, bob, alice, charlie, 1);139    });140  });141142  /* it('transferFrom for a collection that was destroyed', async () => {143    await usingApi(async (api: ApiPromise) => {144      this test copies approve negative test145    });146  }); */147148  /* it('transferFrom a token that does not exist', async () => {149    await usingApi(async (api: ApiPromise) => {150      this test copies approve negative test151    });152  }); */153154  /* it('transferFrom a token that was deleted', async () => {155    await usingApi(async (api: ApiPromise) => {156      this test copies approve negative test157    });158  }); */159160  it('transferFrom for not approved address', async () => {161    await usingApi(async () => {162      // nft163      const nftCollectionId = await createCollectionExpectSuccess();164      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');165166      await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1);167168      // fungible169      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});170      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');171      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1);172      // reFungible173      const reFungibleCollectionId = await174      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});175      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');176      await transferFromExpectFail(177        reFungibleCollectionId,178        newReFungibleTokenId,179        bob,180        alice,181        charlie,182        1,183      );184    });185  });186187  it('transferFrom incorrect token count', async () => {188    await usingApi(async () => {189      // nft190      const nftCollectionId = await createCollectionExpectSuccess();191      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');192      await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address);193194      await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 2);195196      // fungible197      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});198      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');199      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);200      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 2);201      // reFungible202      const reFungibleCollectionId = await203      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});204      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');205      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address);206      await transferFromExpectFail(207        reFungibleCollectionId,208        newReFungibleTokenId,209        bob,210        alice,211        charlie,212        2,213      );214    });215  });216217  it('execute transferFrom from account that is not owner of collection', async () => {218    await usingApi(async (api, privateKeyWrapper) => {219      const dave = privateKeyWrapper('//Dave');220      // nft221      const nftCollectionId = await createCollectionExpectSuccess();222      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');223      try {224        await approveExpectFail(nftCollectionId, newNftTokenId, dave, bob);225        await transferFromExpectFail(nftCollectionId, newNftTokenId, dave, alice, charlie, 1);226      } catch (e) {227        // tslint:disable-next-line:no-unused-expression228        expect(e).to.be.exist;229      }230231      // await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1);232233      // fungible234      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});235      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');236      try {237        await approveExpectFail(fungibleCollectionId, newFungibleTokenId, dave, bob);238        await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, dave, alice, charlie, 1);239      } catch (e) {240        // tslint:disable-next-line:no-unused-expression241        expect(e).to.be.exist;242      }243      // reFungible244      const reFungibleCollectionId = await245      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});246      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');247      try {248        await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, dave, bob);249        await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, dave, alice, charlie, 1);250      } catch (e) {251        // tslint:disable-next-line:no-unused-expression252        expect(e).to.be.exist;253      }254    });255  });256  it('transferFrom burnt token before approve NFT', async () => {257    await usingApi(async () => {258      // nft259      const nftCollectionId = await createCollectionExpectSuccess();260      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');261      await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);262      await approveExpectFail(nftCollectionId, newNftTokenId, alice, bob);263      await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1);264    });265  });266  it('transferFrom burnt token before approve Fungible', async () => {267    await usingApi(async () => {268      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});269      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');270      await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);271      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);272      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1);273274    });275  });276  it('transferFrom burnt token before approve ReFungible', async () => {277    await usingApi(async () => {278      const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});279      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');280      await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);281      await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, alice, bob);282      await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, alice, charlie, 1);283284    });285  });286287  it('transferFrom burnt token after approve NFT', async () => {288    await usingApi(async () => {289      // nft290      const nftCollectionId = await createCollectionExpectSuccess();291      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');292      await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address);293      await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);294      await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1);295    });296  });297  it('transferFrom burnt token after approve Fungible', async () => {298    await usingApi(async () => {299      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});300      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');301      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);302      await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);303      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1);304305    });306  });307  it('transferFrom burnt token after approve ReFungible', async () => {308    await usingApi(async () => {309      const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});310      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');311      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address);312      await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);313      await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, alice, charlie, 1);314315    });316  });317318  it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {319    const collectionId = await createCollectionExpectSuccess();320    const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);321    await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: false});322323    await transferFromExpectFail(collectionId, itemId, alice, bob, charlie);324  });325});
after · tests/src/transferFrom.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {ApiPromise} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import chai from 'chai';20import chaiAsPromised from 'chai-as-promised';21import {default as usingApi} from './substrate/substrate-api';22import {23  approveExpectFail,24  approveExpectSuccess,25  createCollectionExpectSuccess,26  createFungibleItemExpectSuccess,27  createItemExpectSuccess,28  getAllowance,29  transferFromExpectFail,30  transferFromExpectSuccess,31  burnItemExpectSuccess,32  setCollectionLimitsExpectSuccess,33  getCreatedCollectionCount,34} from './util/helpers';3536chai.use(chaiAsPromised);37const expect = chai.expect;3839describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {40  let alice: IKeyringPair;41  let bob: IKeyringPair;42  let charlie: IKeyringPair;4344  before(async () => {45    await usingApi(async (api, privateKeyWrapper) => {46      alice = privateKeyWrapper('//Alice');47      bob = privateKeyWrapper('//Bob');48      charlie = privateKeyWrapper('//Charlie');49    });50  });5152  it('Execute the extrinsic and check nftItemList - owner of token', async () => {53    await usingApi(async () => {54      // nft55      const nftCollectionId = await createCollectionExpectSuccess();56      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');57      await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address);5859      await transferFromExpectSuccess(nftCollectionId, newNftTokenId, bob, alice, charlie, 1, 'NFT');6061      // fungible62      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});63      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');64      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);65      await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1, 'Fungible');6667      // reFungible68      const reFungibleCollectionId = await69      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});70      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');71      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 100);72      await transferFromExpectSuccess(73        reFungibleCollectionId,74        newReFungibleTokenId,75        bob,76        alice,77        charlie,78        100,79        'ReFungible',80      );81    });82  });8384  it('Should reduce allowance if value is big', async () => {85    await usingApi(async (api, privateKeyWrapper) => {86      const alice = privateKeyWrapper('//Alice');87      const bob = privateKeyWrapper('//Bob');88      const charlie = privateKeyWrapper('//Charlie');8990      // fungible91      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});92      const newFungibleTokenId = await createFungibleItemExpectSuccess(alice, fungibleCollectionId, {Value: 500000n});9394      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address, 500000n);95      await transferFromExpectSuccess(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 500000n, 'Fungible');96      expect(await getAllowance(api, fungibleCollectionId, alice.address, bob.address, newFungibleTokenId)).to.equal(0n);97    });98  });99100  it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {101    const collectionId = await createCollectionExpectSuccess();102    await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});103    const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);104105    await transferFromExpectSuccess(collectionId, itemId, alice, bob, charlie);106  });107});108109describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {110  let alice: IKeyringPair;111  let bob: IKeyringPair;112  let charlie: IKeyringPair;113114  before(async () => {115    await usingApi(async (api, privateKeyWrapper) => {116      alice = privateKeyWrapper('//Alice');117      bob = privateKeyWrapper('//Bob');118      charlie = privateKeyWrapper('//Charlie');119    });120  });121122  it('transferFrom for a collection that does not exist', async () => {123    await usingApi(async (api: ApiPromise) => {124      // nft125      const nftCollectionCount = await getCreatedCollectionCount(api);126      await approveExpectFail(nftCollectionCount + 1, 1, alice, bob);127128      await transferFromExpectFail(nftCollectionCount + 1, 1, bob, alice, charlie, 1);129130      // fungible131      const fungibleCollectionCount = await getCreatedCollectionCount(api);132      await approveExpectFail(fungibleCollectionCount + 1, 0, alice, bob);133134      await transferFromExpectFail(fungibleCollectionCount + 1, 0, bob, alice, charlie, 1);135      // reFungible136      const reFungibleCollectionCount = await getCreatedCollectionCount(api);137      await approveExpectFail(reFungibleCollectionCount + 1, 1, alice, bob);138139      await transferFromExpectFail(reFungibleCollectionCount + 1, 1, bob, alice, charlie, 1);140    });141  });142143  /* it('transferFrom for a collection that was destroyed', async () => {144    await usingApi(async (api: ApiPromise) => {145      this test copies approve negative test146    });147  }); */148149  /* it('transferFrom a token that does not exist', async () => {150    await usingApi(async (api: ApiPromise) => {151      this test copies approve negative test152    });153  }); */154155  /* it('transferFrom a token that was deleted', async () => {156    await usingApi(async (api: ApiPromise) => {157      this test copies approve negative test158    });159  }); */160161  it('transferFrom for not approved address', async () => {162    await usingApi(async () => {163      // nft164      const nftCollectionId = await createCollectionExpectSuccess();165      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');166167      await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1);168169      // fungible170      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});171      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');172      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1);173      // reFungible174      const reFungibleCollectionId = await175      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});176      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');177      await transferFromExpectFail(178        reFungibleCollectionId,179        newReFungibleTokenId,180        bob,181        alice,182        charlie,183        1,184      );185    });186  });187188  it('transferFrom incorrect token count', async () => {189    await usingApi(async () => {190      // nft191      const nftCollectionId = await createCollectionExpectSuccess();192      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');193      await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address);194195      await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 2);196197      // fungible198      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});199      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');200      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);201      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 2);202      // reFungible203      const reFungibleCollectionId = await204      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});205      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');206      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address);207      await transferFromExpectFail(208        reFungibleCollectionId,209        newReFungibleTokenId,210        bob,211        alice,212        charlie,213        2,214      );215    });216  });217218  it('execute transferFrom from account that is not owner of collection', async () => {219    await usingApi(async (api, privateKeyWrapper) => {220      const dave = privateKeyWrapper('//Dave');221      // nft222      const nftCollectionId = await createCollectionExpectSuccess();223      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');224      try {225        await approveExpectFail(nftCollectionId, newNftTokenId, dave, bob);226        await transferFromExpectFail(nftCollectionId, newNftTokenId, dave, alice, charlie, 1);227      } catch (e) {228        // tslint:disable-next-line:no-unused-expression229        expect(e).to.be.exist;230      }231232      // await transferFromExpectFail(nftCollectionId, newNftTokenId, Dave, Alice, Charlie, 1);233234      // fungible235      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});236      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');237      try {238        await approveExpectFail(fungibleCollectionId, newFungibleTokenId, dave, bob);239        await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, dave, alice, charlie, 1);240      } catch (e) {241        // tslint:disable-next-line:no-unused-expression242        expect(e).to.be.exist;243      }244      // reFungible245      const reFungibleCollectionId = await246      createCollectionExpectSuccess({mode: {type: 'ReFungible'}});247      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');248      try {249        await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, dave, bob);250        await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, dave, alice, charlie, 1);251      } catch (e) {252        // tslint:disable-next-line:no-unused-expression253        expect(e).to.be.exist;254      }255    });256  });257  it('transferFrom burnt token before approve NFT', async () => {258    await usingApi(async () => {259      // nft260      const nftCollectionId = await createCollectionExpectSuccess();261      await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {ownerCanTransfer: true});262      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');263      await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);264      await approveExpectFail(nftCollectionId, newNftTokenId, alice, bob);265      await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1);266    });267  });268  it('transferFrom burnt token before approve Fungible', async () => {269    await usingApi(async () => {270      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});271      await setCollectionLimitsExpectSuccess(alice, fungibleCollectionId, {ownerCanTransfer: true});272      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');273      await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);274      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);275      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1);276277    });278  });279  it('transferFrom burnt token before approve ReFungible', async () => {280    await usingApi(async () => {281      const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});282      await setCollectionLimitsExpectSuccess(alice, reFungibleCollectionId, {ownerCanTransfer: true});283      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');284      await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);285      await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, alice, bob);286      await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, alice, charlie, 1);287288    });289  });290291  it('transferFrom burnt token after approve NFT', async () => {292    await usingApi(async () => {293      // nft294      const nftCollectionId = await createCollectionExpectSuccess();295      const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');296      await approveExpectSuccess(nftCollectionId, newNftTokenId, alice, bob.address);297      await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);298      await transferFromExpectFail(nftCollectionId, newNftTokenId, bob, alice, charlie, 1);299    });300  });301  it('transferFrom burnt token after approve Fungible', async () => {302    await usingApi(async () => {303      const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});304      const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');305      await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);306      await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);307      await transferFromExpectFail(fungibleCollectionId, newFungibleTokenId, bob, alice, charlie, 1);308309    });310  });311  it('transferFrom burnt token after approve ReFungible', async () => {312    await usingApi(async () => {313      const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});314      const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');315      await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address);316      await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);317      await transferFromExpectFail(reFungibleCollectionId, newReFungibleTokenId, bob, alice, charlie, 1);318319    });320  });321322  it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {323    const collectionId = await createCollectionExpectSuccess();324    const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);325    await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: false});326327    await transferFromExpectFail(collectionId, itemId, alice, bob, charlie);328  });329});
modifiedtests/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;