difftreelog
OwnerCanTransfer flag
in: master
10 files changed
pallets/common/src/lib.rsdiffbeforeafterboth1164 old_limit: &CollectionLimits,1164 old_limit: &CollectionLimits,1165 mut new_limit: CollectionLimits,1165 mut new_limit: CollectionLimits,1166 ) -> Result<CollectionLimits, DispatchError> {1166 ) -> Result<CollectionLimits, DispatchError> {1167 let limits = old_limit;1167 limit_default!(old_limit, new_limit,1168 limit_default!(old_limit, new_limit,1168 account_token_ownership_limit => ensure!(1169 account_token_ownership_limit => ensure!(1169 new_limit <= MAX_TOKEN_OWNERSHIP,1170 new_limit <= MAX_TOKEN_OWNERSHIP,1190 ),1191 ),1191 sponsor_approve_timeout => {},1192 sponsor_approve_timeout => {},1192 owner_can_transfer => ensure!(1193 owner_can_transfer => ensure!(1194 !limits.owner_can_transfer_instaled() ||1193 old_limit || !new_limit,1195 old_limit || !new_limit,1194 <Error<T>>::OwnerPermissionsCantBeReverted,1196 <Error<T>>::OwnerPermissionsCantBeReverted,1195 ),1197 ),primitives/data-structs/src/lib.rsdiffbeforeafterboth409 .min(MAX_SPONSOR_TIMEOUT)409 .min(MAX_SPONSOR_TIMEOUT)410 }410 }411 pub fn owner_can_transfer(&self) -> bool {411 pub fn owner_can_transfer(&self) -> bool {412 self.owner_can_transfer.unwrap_or(true)412 self.owner_can_transfer.unwrap_or(false)413 }413 }414 pub fn owner_can_transfer_instaled(&self) -> bool {415 self.owner_can_transfer.is_some()416 }414 pub fn owner_can_destroy(&self) -> bool {417 pub fn owner_can_destroy(&self) -> bool {415 self.owner_can_destroy.unwrap_or(true)418 self.owner_can_destroy.unwrap_or(true)416 }419 }tests/src/approve.test.tsdiffbeforeafterboth28 setCollectionLimitsExpectSuccess,28 setCollectionLimitsExpectSuccess,29 transferExpectSuccess,29 transferExpectSuccess,30 addCollectionAdminExpectSuccess,30 addCollectionAdminExpectSuccess,31 adminApproveFromExpectSuccess,31 adminApproveFromExpectFail,32 getCreatedCollectionCount,32 getCreatedCollectionCount,33 transferFromExpectSuccess,33 transferFromExpectSuccess,34 transferFromExpectFail,34 transferFromExpectFail,84 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);84 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, alice, bob.address, 0);85 });85 });868687 it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {87 it('can`t be called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {88 const collectionId = await createCollectionExpectSuccess();88 const collectionId = await createCollectionExpectSuccess();89 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);89 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);909091 await adminApproveFromExpectSuccess(collectionId, itemId, alice, bob.address, charlie.address);91 await adminApproveFromExpectFail(collectionId, itemId, alice, bob.address, charlie.address);92 });92 });93});93});9494292 });292 });293});293});294294295describe('Administrator and collection owner do not need approval in order to execute TransferFrom:', () => {295describe('Administrator and collection owner do not need approval in order to execute TransferFrom (with owner_can_transfer_flag = true):', () => {296 let alice: IKeyringPair;296 let alice: IKeyringPair;297 let bob: IKeyringPair;297 let bob: IKeyringPair;298 let charlie: IKeyringPair;298 let charlie: IKeyringPair;309309310 it('NFT', async () => {310 it('NFT', async () => {311 const collectionId = await createCollectionExpectSuccess();311 const collectionId = await createCollectionExpectSuccess();312 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});312 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', charlie.address);313 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', charlie.address);313 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'NFT');314 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'NFT');314 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);315 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);317318318 it('Fungible up to an approved amount', async () => {319 it('Fungible up to an approved amount', async () => {319 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});320 const collectionId = await createCollectionExpectSuccess({mode:{type: 'Fungible', decimalPoints: 0}});321 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});320 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', charlie.address); 322 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', charlie.address); 321 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'Fungible');323 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'Fungible');322 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);324 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);325327326 it('ReFungible up to an approved amount', async () => {328 it('ReFungible up to an approved amount', async () => {327 const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});329 const collectionId = await createCollectionExpectSuccess({mode:{type: 'ReFungible'}});330 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});328 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', charlie.address);331 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', charlie.address);329 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'ReFungible');332 await transferFromExpectSuccess(collectionId, itemId, alice, charlie, dave, 1, 'ReFungible');330 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);333 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);402 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);405 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);403406404 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);407 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);405 await adminApproveFromExpectSuccess(collectionId, itemId, bob, alice.address, charlie.address);408 await adminApproveFromExpectFail(collectionId, itemId, bob, alice.address, charlie.address);406 });409 });407});410});408411tests/src/burnItem.test.tsdiffbeforeafterboth23 normalizeAccountId,23 normalizeAccountId,24 addCollectionAdminExpectSuccess,24 addCollectionAdminExpectSuccess,25 getBalance,25 getBalance,26 setCollectionLimitsExpectSuccess,26 isTokenExists,27 isTokenExists,27} from './util/helpers';28} from './util/helpers';2829149 it('Burn item in NFT collection', async () => {150 it('Burn item in NFT collection', async () => {150 const createMode = 'NFT';151 const createMode = 'NFT';151 const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});152 const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});153 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});152 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);154 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);153 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);155 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);154156167 it('Burn item in Fungible collection', async () => {169 it('Burn item in Fungible collection', async () => {168 const createMode = 'Fungible';170 const createMode = 'Fungible';169 const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});171 const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});172 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});170 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); // Helper creates 10 fungible tokens173 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode); // Helper creates 10 fungible tokens171 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);174 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);172175189 it('Burn item in ReFungible collection', async () => {192 it('Burn item in ReFungible collection', async () => {190 const createMode = 'ReFungible';193 const createMode = 'ReFungible';191 const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});194 const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});195 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});192 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);196 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode);193 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);197 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);194198tests/src/eth/crossTransfer.test.tsdiffbeforeafterboth18 createFungibleItemExpectSuccess,18 createFungibleItemExpectSuccess,19 transferExpectSuccess,19 transferExpectSuccess,20 transferFromExpectSuccess,20 transferFromExpectSuccess,21 setCollectionLimitsExpectSuccess,21 createItemExpectSuccess} from '../util/helpers';22 createItemExpectSuccess} from '../util/helpers';22import {collectionIdToAddress,23import {collectionIdToAddress,23 createEthAccountWithBalance,24 createEthAccountWithBalance,35 const alice = privateKeyWrapper('//Alice');36 const alice = privateKeyWrapper('//Alice');36 const bob = privateKeyWrapper('//Bob');37 const bob = privateKeyWrapper('//Bob');37 const charlie = privateKeyWrapper('//Charlie');38 const charlie = privateKeyWrapper('//Charlie');39 await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});38 await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Substrate: alice.address});40 await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Substrate: alice.address});39 await transferExpectSuccess(collection, 0, alice, {Ethereum: subToEth(charlie.address)} , 200, 'Fungible');41 await transferExpectSuccess(collection, 0, alice, {Ethereum: subToEth(charlie.address)} , 200, 'Fungible');40 await transferFromExpectSuccess(collection, 0, alice, {Ethereum: subToEth(charlie.address)}, charlie, 50, 'Fungible');42 await transferFromExpectSuccess(collection, 0, alice, {Ethereum: subToEth(charlie.address)}, charlie, 50, 'Fungible');48 });50 });49 const alice = privateKeyWrapper('//Alice');51 const alice = privateKeyWrapper('//Alice');50 const bob = privateKeyWrapper('//Bob');52 const bob = privateKeyWrapper('//Bob');53 await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});51 const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);54 const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);52 const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);55 const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);535671 const alice = privateKeyWrapper('//Alice');74 const alice = privateKeyWrapper('//Alice');72 const bob = privateKeyWrapper('//Bob');75 const bob = privateKeyWrapper('//Bob');73 const charlie = privateKeyWrapper('//Charlie');76 const charlie = privateKeyWrapper('//Charlie');77 await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});74 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});78 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});75 await transferExpectSuccess(collection, tokenId, alice, {Ethereum: subToEth(charlie.address)}, 1, 'NFT');79 await transferExpectSuccess(collection, tokenId, alice, {Ethereum: subToEth(charlie.address)}, 1, 'NFT');76 await transferFromExpectSuccess(collection, tokenId, alice, {Ethereum: subToEth(charlie.address)}, charlie, 1, 'NFT');80 await transferFromExpectSuccess(collection, tokenId, alice, {Ethereum: subToEth(charlie.address)}, charlie, 1, 'NFT');85 const alice = privateKeyWrapper('//Alice');89 const alice = privateKeyWrapper('//Alice');86 const bob = privateKeyWrapper('//Bob');90 const bob = privateKeyWrapper('//Bob');87 const charlie = privateKeyWrapper('//Charlie');91 const charlie = privateKeyWrapper('//Charlie');92 await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});88 const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);93 const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);89 const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);94 const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);90 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});95 const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});tests/src/limits.test.tsdiffbeforeafterboth406 it('Effective collection limits', async () => {406 it('Effective collection limits', async () => {407 await usingApi(async (api) => {407 await usingApi(async (api) => {408 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});408 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});409 409 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});410 410 { // Check that limits is undefined411 { // Check that limits is undefined411 const collection = await api.rpc.unique.collectionById(collectionId);412 const collection = await api.rpc.unique.collectionById(collectionId);419 expect(limits.tokenLimit.toHuman()).to.be.null;420 expect(limits.tokenLimit.toHuman()).to.be.null;420 expect(limits.sponsorTransferTimeout.toHuman()).to.be.null;421 expect(limits.sponsorTransferTimeout.toHuman()).to.be.null;421 expect(limits.sponsorApproveTimeout.toHuman()).to.be.null;422 expect(limits.sponsorApproveTimeout.toHuman()).to.be.null;422 expect(limits.ownerCanTransfer.toHuman()).to.be.null;423 expect(limits.ownerCanTransfer.toHuman()).to.be.true;423 expect(limits.ownerCanDestroy.toHuman()).to.be.null;424 expect(limits.ownerCanDestroy.toHuman()).to.be.null;424 expect(limits.transfersEnabled.toHuman()).to.be.null;425 expect(limits.transfersEnabled.toHuman()).to.be.null;425 }426 }tests/src/nesting/graphs.test.tsdiffbeforeafterboth3import {expect} from 'chai';3import {expect} from 'chai';4import {tokenIdToCross} from '../eth/util/helpers';4import {tokenIdToCross} from '../eth/util/helpers';5import usingApi, {executeTransaction} from '../substrate/substrate-api';5import usingApi, {executeTransaction} from '../substrate/substrate-api';6import {getCreateCollectionResult, transferExpectSuccess} from '../util/helpers';6import {getCreateCollectionResult, transferExpectSuccess, setCollectionLimitsExpectSuccess} from '../util/helpers';778/**8/**9 * ```dot9 * ```dot36 await usingApi(async (api, privateKeyWrapper) => {36 await usingApi(async (api, privateKeyWrapper) => {37 const alice = privateKeyWrapper('//Alice');37 const alice = privateKeyWrapper('//Alice');38 const collection = await buildComplexObjectGraph(api, alice);38 const collection = await buildComplexObjectGraph(api, alice);39 await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true});394040 // to self41 // to self41 await expect(42 await expect(tests/src/nesting/nest.test.tsdiffbeforeafterboth15 transferExpectFailure,15 transferExpectFailure,16 transferExpectSuccess,16 transferExpectSuccess,17 transferFromExpectSuccess,17 transferFromExpectSuccess,18 setCollectionLimitsExpectSuccess,18} from '../util/helpers';19} from '../util/helpers';19import {IKeyringPair} from '@polkadot/types/types';20import {IKeyringPair} from '@polkadot/types/types';202192 it('Checks token children', async () => {93 it('Checks token children', async () => {93 await usingApi(async api => {94 await usingApi(async api => {94 const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});95 const collectionA = await createCollectionExpectSuccess({mode: {type: 'NFT'}});96 await setCollectionLimitsExpectSuccess(alice, collectionA, {ownerCanTransfer: true});95 await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {tokenOwner: true}});97 await setCollectionPermissionsExpectSuccess(alice, collectionA, {nesting: {tokenOwner: true}});96 const collectionB = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});98 const collectionB = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});9799tests/src/transferFrom.test.tsdiffbeforeafterboth9999100 it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {100 it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {101 const collectionId = await createCollectionExpectSuccess();101 const collectionId = await createCollectionExpectSuccess();102 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true});102 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);103 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', bob.address);103104104 await transferFromExpectSuccess(collectionId, itemId, alice, bob, charlie);105 await transferFromExpectSuccess(collectionId, itemId, alice, bob, charlie);257 await usingApi(async () => {258 await usingApi(async () => {258 // nft259 // nft259 const nftCollectionId = await createCollectionExpectSuccess();260 const nftCollectionId = await createCollectionExpectSuccess();261 await setCollectionLimitsExpectSuccess(alice, nftCollectionId, {ownerCanTransfer: true});260 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');262 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');261 await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);263 await burnItemExpectSuccess(alice, nftCollectionId, newNftTokenId, 1);262 await approveExpectFail(nftCollectionId, newNftTokenId, alice, bob);264 await approveExpectFail(nftCollectionId, newNftTokenId, alice, bob);266 it('transferFrom burnt token before approve Fungible', async () => {268 it('transferFrom burnt token before approve Fungible', async () => {267 await usingApi(async () => {269 await usingApi(async () => {268 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});270 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});271 await setCollectionLimitsExpectSuccess(alice, fungibleCollectionId, {ownerCanTransfer: true});269 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');272 const newFungibleTokenId = await createItemExpectSuccess(alice, fungibleCollectionId, 'Fungible');270 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);273 await burnItemExpectSuccess(alice, fungibleCollectionId, newFungibleTokenId, 10);271 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);274 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, alice, bob.address);276 it('transferFrom burnt token before approve ReFungible', async () => {279 it('transferFrom burnt token before approve ReFungible', async () => {277 await usingApi(async () => {280 await usingApi(async () => {278 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});281 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});282 await setCollectionLimitsExpectSuccess(alice, reFungibleCollectionId, {ownerCanTransfer: true});279 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');283 const newReFungibleTokenId = await createItemExpectSuccess(alice, reFungibleCollectionId, 'ReFungible');280 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);284 await burnItemExpectSuccess(alice, reFungibleCollectionId, newReFungibleTokenId, 100);281 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, alice, bob);285 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, alice, bob);tests/src/util/helpers.tsdiffbeforeafterboth921 });921 });922}922}923924export async function adminApproveFromExpectFail(925 collectionId: number,926 tokenId: number, admin: IKeyringPair, owner: CrossAccountId | string, approved: CrossAccountId | string, amount: number | bigint = 1,927) {928 await usingApi(async (api: ApiPromise) => {929 const approveUniqueTx = api.tx.unique.approve(normalizeAccountId(approved), collectionId, tokenId, amount);930 const events = await expect(submitTransactionAsync(admin, approveUniqueTx)).to.be.rejected;931 const result = getGenericResult(events);932 expect(result.success).to.be.false;933 });934}923935924export async function936export async function925getFreeBalance(account: IKeyringPair): Promise<bigint> {937getFreeBalance(account: IKeyringPair): Promise<bigint> {