From 6b48a5a77b12994aca0b4c4e99ac6ef74d65aa17 Mon Sep 17 00:00:00 2001 From: Dev Date: Tue, 14 Jun 2022 14:04:00 +0000 Subject: [PATCH] OwnerCanTransfer flag --- --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -1164,6 +1164,7 @@ old_limit: &CollectionLimits, mut new_limit: CollectionLimits, ) -> Result { + 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, >::OwnerPermissionsCantBeReverted, ), --- 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) --- 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); }); }); --- 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); --- 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}); --- 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; } --- 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( --- 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}}); --- 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); --- 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 { let balance = 0n; -- gitstuff