difftreelog
Merge remote-tracking branch 'origin/master' into develop
in: master
2 files changed
pallets/nft/src/lib.rsdiffbeforeafterboth--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -1110,14 +1110,23 @@
// Transfer permissions check
let target_collection = <Collection<T>>::get(collection_id);
- ensure!(
- Self::is_item_owner(sender.clone(), collection_id, item_id) ||
- (
- target_collection.limits.owner_can_transfer &&
- Self::is_owner_or_admin_permissions(collection_id, sender.clone())
- ),
- Error::<T>::NoPermission
- );
+ let allowance_limit = if (
+ target_collection.limits.owner_can_transfer &&
+ Self::is_owner_or_admin_permissions(
+ collection_id,
+ sender.clone(),
+ )
+ ) {
+ None
+ } else if let Some(amount) = Self::owned_amount(
+ sender.clone(),
+ collection_id,
+ item_id,
+ ) {
+ Some(amount)
+ } else {
+ fail!(Error::<T>::NoPermission);
+ };
if target_collection.access == AccessMode::WhiteList {
Self::check_white_list(collection_id, &sender)?;
@@ -1129,6 +1138,9 @@
if allowance_exists {
allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));
}
+ if let Some(limit) = allowance_limit {
+ ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);
+ }
<Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);
Ok(())
@@ -1912,6 +1924,36 @@
Ok(())
}
+ fn owned_amount(
+ subject: T::AccountId,
+ collection_id: CollectionId,
+ item_id: TokenId,
+ ) -> Option<u128> {
+ let target_collection = <Collection<T>>::get(collection_id);
+
+ match target_collection.mode {
+ CollectionMode::NFT => {
+ if <NftItemList<T>>::get(collection_id, item_id).owner == subject {
+ return Some(1)
+ }
+ None
+ },
+ CollectionMode::Fungible(_) => {
+ if <FungibleItemList<T>>::contains_key(collection_id, &subject) {
+ return Some(<FungibleItemList<T>>::get(collection_id, &subject)
+ .value);
+ }
+ None
+ },
+ CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)
+ .owner
+ .iter()
+ .find(|i| i.owner == subject)
+ .map(|i| i.fraction),
+ CollectionMode::Invalid => None,
+ }
+ }
+
fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {
let target_collection = <Collection<T>>::get(collection_id);
tests/src/approve.test.tsdiffbeforeafterboth1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//5import { ApiPromise } from '@polkadot/api';6import { IKeyringPair } from '@polkadot/types/types';7import BN from 'bn.js';8import chai from 'chai';9import chaiAsPromised from 'chai-as-promised';10import privateKey from './substrate/privateKey';11import { default as usingApi } from './substrate/substrate-api';12import {13 approveExpectFail,14 approveExpectSuccess,15 createCollectionExpectSuccess,16 createFungibleItemExpectSuccess,17 createItemExpectSuccess,18 destroyCollectionExpectSuccess,19 setCollectionLimitsExpectSuccess,20 transferFromExpectSuccess,21 U128_MAX,22} from './util/helpers';2324chai.use(chaiAsPromised);25const expect = chai.expect;2627describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {28 let Alice: IKeyringPair;29 let Bob: IKeyringPair;30 let Charlie: IKeyringPair;3132 before(async () => {33 await usingApi(async (api) => {34 Alice = privateKey('//Alice');35 Bob = privateKey('//Bob');36 Charlie = privateKey('//Charlie');37 });38 });3940 it('Execute the extrinsic and check approvedList', async () => {41 await usingApi(async (api: ApiPromise) => {42 const nftCollectionId = await createCollectionExpectSuccess();43 // nft44 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');45 await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);46 // fungible47 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});48 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');49 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);50 // reFungible51 const reFungibleCollectionId =52 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});53 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');54 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);55 });56 });5758 it('Remove approval by using 0 amount', async () => {59 await usingApi(async (api: ApiPromise) => {60 const nftCollectionId = await createCollectionExpectSuccess();61 // nft62 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');63 await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1);64 await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 0);65 // fungible66 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});67 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');68 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1);69 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0);70 // reFungible71 const reFungibleCollectionId =72 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});73 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');74 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1);75 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0);76 });77 });7879 it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {80 const collectionId = await createCollectionExpectSuccess();81 const itemId = await createItemExpectSuccess(Alice, collectionId, 'NFT', Bob.address);8283 await approveExpectSuccess(collectionId, itemId, Alice, Charlie);84 });85});8687describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {88 let Alice: IKeyringPair;89 let Bob: IKeyringPair;90 let Charlie: IKeyringPair;9192 before(async () => {93 await usingApi(async (api) => {94 Alice = privateKey('//Alice');95 Bob = privateKey('//Bob');96 Charlie = privateKey('//Charlie');97 });98 });99100 it('Approve for a collection that does not exist', async () => {101 await usingApi(async (api: ApiPromise) => {102 // nft103 const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;104 await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);105 // fungible106 const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;107 await approveExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob);108 // reFungible109 const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;110 await approveExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob);111 });112 });113114 it('Approve for a collection that was destroyed', async () => {115 await usingApi(async (api: ApiPromise) => {116 // nft117 const nftCollectionId = await createCollectionExpectSuccess();118 await destroyCollectionExpectSuccess(nftCollectionId);119 await approveExpectFail(nftCollectionId, 1, Alice, Bob);120 // fungible121 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});122 await destroyCollectionExpectSuccess(fungibleCollectionId);123 await approveExpectFail(fungibleCollectionId, 1, Alice, Bob);124 // reFungible125 const reFungibleCollectionId =126 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});127 await destroyCollectionExpectSuccess(reFungibleCollectionId);128 await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob);129 });130 });131132 it('Approve transfer of a token that does not exist', async () => {133 await usingApi(async (api: ApiPromise) => {134 // nft135 const nftCollectionId = await createCollectionExpectSuccess();136 await approveExpectFail(nftCollectionId, 2, Alice, Bob);137 // fungible138 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});139 await approveExpectFail(fungibleCollectionId, 2, Alice, Bob);140 // reFungible141 const reFungibleCollectionId =142 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});143 await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob);144 });145 });146147 it('Approve using the address that does not own the approved token', async () => {148 await usingApi(async (api: ApiPromise) => {149 const nftCollectionId = await createCollectionExpectSuccess();150 // nft151 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');152 await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice);153 // fungible154 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});155 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');156 await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice);157 // reFungible158 const reFungibleCollectionId =159 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});160 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');161 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice);162 });163 });164165 it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {166 const collectionId = await createCollectionExpectSuccess();167 const itemId = await createItemExpectSuccess(Alice, collectionId, 'NFT', Bob.address);168 await setCollectionLimitsExpectSuccess(Alice, collectionId, { OwnerCanTransfer: false });169170 await approveExpectFail(collectionId, itemId, Alice, Charlie);171 });172});1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//5import { IKeyringPair } from '@polkadot/types/types';6import { ApiPromise } from '@polkadot/api';7import chai from 'chai';8import chaiAsPromised from 'chai-as-promised';9import privateKey from './substrate/privateKey';10import { default as usingApi } from './substrate/substrate-api';11import {12 approveExpectFail,13 approveExpectSuccess,14 createCollectionExpectSuccess,15 createItemExpectSuccess,16 destroyCollectionExpectSuccess,17 setCollectionLimitsExpectSuccess,18 transferExpectSuccess,19} from './util/helpers';2021chai.use(chaiAsPromised);2223describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {24 let Alice: IKeyringPair;25 let Bob: IKeyringPair;26 let Charlie: IKeyringPair;2728 before(async () => {29 await usingApi(async () => {30 Alice = privateKey('//Alice');31 Bob = privateKey('//Bob');32 Charlie = privateKey('//Charlie');33 });34 });3536 it('Execute the extrinsic and check approvedList', async () => {37 const nftCollectionId = await createCollectionExpectSuccess();38 // nft39 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');40 await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob);41 // fungible42 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});43 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');44 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);45 // reFungible46 const reFungibleCollectionId =47 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});48 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');49 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);50 });5152 it('Remove approval by using 0 amount', async () => {53 const nftCollectionId = await createCollectionExpectSuccess();54 // nft55 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');56 await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1);57 await approveExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 0);58 // fungible59 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});60 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');61 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 1);62 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob, 0);63 // reFungible64 const reFungibleCollectionId =65 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});66 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');67 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 1);68 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob, 0);69 });7071 it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {72 const collectionId = await createCollectionExpectSuccess();73 const itemId = await createItemExpectSuccess(Alice, collectionId, 'NFT', Bob.address);7475 await approveExpectSuccess(collectionId, itemId, Alice, Charlie);76 });77});7879describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {80 let Alice: IKeyringPair;81 let Bob: IKeyringPair;82 let Charlie: IKeyringPair;8384 before(async () => {85 await usingApi(async (api) => {86 Alice = privateKey('//Alice');87 Bob = privateKey('//Bob');88 Charlie = privateKey('//Charlie');89 });90 });9192 it('Approve for a collection that does not exist', async () => {93 await usingApi(async (api: ApiPromise) => {94 // nft95 const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;96 await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);97 // fungible98 const fungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;99 await approveExpectFail(fungibleCollectionCount + 1, 1, Alice, Bob);100 // reFungible101 const reFungibleCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;102 await approveExpectFail(reFungibleCollectionCount + 1, 1, Alice, Bob);103 });104 });105106 it('Approve for a collection that was destroyed', async () => {107 // nft108 const nftCollectionId = await createCollectionExpectSuccess();109 await destroyCollectionExpectSuccess(nftCollectionId);110 await approveExpectFail(nftCollectionId, 1, Alice, Bob);111 // fungible112 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});113 await destroyCollectionExpectSuccess(fungibleCollectionId);114 await approveExpectFail(fungibleCollectionId, 1, Alice, Bob);115 // reFungible116 const reFungibleCollectionId =117 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});118 await destroyCollectionExpectSuccess(reFungibleCollectionId);119 await approveExpectFail(reFungibleCollectionId, 1, Alice, Bob);120 });121122 it('Approve transfer of a token that does not exist', async () => {123 // nft124 const nftCollectionId = await createCollectionExpectSuccess();125 await approveExpectFail(nftCollectionId, 2, Alice, Bob);126 // fungible127 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});128 await approveExpectFail(fungibleCollectionId, 2, Alice, Bob);129 // reFungible130 const reFungibleCollectionId =131 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});132 await approveExpectFail(reFungibleCollectionId, 2, Alice, Bob);133 });134135 it('Approve using the address that does not own the approved token', async () => {136 const nftCollectionId = await createCollectionExpectSuccess();137 // nft138 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');139 await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice);140 // fungible141 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});142 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');143 await approveExpectFail(fungibleCollectionId, newFungibleTokenId, Bob, Alice);144 // reFungible145 const reFungibleCollectionId =146 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});147 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');148 await approveExpectFail(reFungibleCollectionId, newReFungibleTokenId, Bob, Alice);149 });150151 it('should fail if approved more NFTs than owned', async () => {152 const nftCollectionId = await createCollectionExpectSuccess({ mode: { type: 'NFT' } });153 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');154 await transferExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 1, 'NFT');155 await approveExpectSuccess(nftCollectionId, newNftTokenId, Bob, Alice);156 await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice);157 });158159 it('should fail if approved more ReFungibles than owned', async () => {160 const nftCollectionId = await createCollectionExpectSuccess({ mode: { type: 'ReFungible' } });161 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'ReFungible');162 await transferExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 100, 'ReFungible');163 await approveExpectSuccess(nftCollectionId, newNftTokenId, Bob, Alice, 100);164 await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice, 1);165 });166167 it('should fail if approved more Fungibles than owned', async () => {168 const nftCollectionId = await createCollectionExpectSuccess({ mode: { type: 'Fungible', decimalPoints: 0 } });169 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'Fungible');170 await transferExpectSuccess(nftCollectionId, newNftTokenId, Alice, Bob, 10, 'Fungible');171 await approveExpectSuccess(nftCollectionId, newNftTokenId, Bob, Alice, 10);172 await approveExpectFail(nftCollectionId, newNftTokenId, Bob, Alice, 1);173 });174175 it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {176 const collectionId = await createCollectionExpectSuccess();177 const itemId = await createItemExpectSuccess(Alice, collectionId, 'NFT', Bob.address);178 await setCollectionLimitsExpectSuccess(Alice, collectionId, { OwnerCanTransfer: false });179180 await approveExpectFail(collectionId, itemId, Alice, Charlie);181 });182});