git.delta.rocks / unique-network / refs/commits / b995e58942f3

difftreelog

Merge pull request #112 from usetech-llc/feature/NFTPAR-308_limited_owners_control

Greg Zaitsev2021-02-26parents: #6ea0c80 #4bce10c.patch.diff
in: master
Limited owners control

7 files changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
382 AccountTokenLimitExceeded,382 AccountTokenLimitExceeded,
383 /// Collection limit bounds per collection exceeded383 /// Collection limit bounds per collection exceeded
384 CollectionLimitBoundsExceeded,384 CollectionLimitBoundsExceeded,
385 /// Tried to enable permissions which are only permitted to be disabled
386 OwnerPermissionsCantBeReverted,
385 /// Schema data size limit bound exceeded387 /// Schema data size limit bound exceeded
386 SchemaDataLimitExceeded,388 SchemaDataLimitExceeded,
387 /// Maximum refungibility exceeded389 /// Maximum refungibility exceeded
639 let sender = ensure_signed(origin)?;641 let sender = ensure_signed(origin)?;
640 Self::check_owner_permissions(collection_id, sender)?;642 Self::check_owner_permissions(collection_id, sender)?;
643
644 let target_collection = <Collection<T>>::get(collection_id);
645 if !target_collection.limits.owner_can_destroy {
646 fail!(Error::<T>::NoPermission);
647 }
641648
642 <AddressTokens<T>>::remove_prefix(collection_id);649 <AddressTokens<T>>::remove_prefix(collection_id);
643 <Allowances<T>>::remove_prefix(collection_id);650 <Allowances<T>>::remove_prefix(collection_id);
1024 let target_collection = <Collection<T>>::get(collection_id);1031 let target_collection = <Collection<T>>::get(collection_id);
1025 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1032 ensure!(
1033 Self::is_item_owner(sender.clone(), collection_id, item_id) ||
1034 (
1035 target_collection.limits.owner_can_transfer &&
1026 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1036 Self::is_owner_or_admin_permissions(collection_id, sender.clone())
1037 ),
1027 Error::<T>::NoPermission);1038 Error::<T>::NoPermission
1039 );
10281040
1100 let target_collection = <Collection<T>>::get(collection_id);1112 let target_collection = <Collection<T>>::get(collection_id);
1101 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1113 ensure!(
1114 Self::is_item_owner(sender.clone(), collection_id, item_id) ||
1115 (
1116 target_collection.limits.owner_can_transfer &&
1102 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1117 Self::is_owner_or_admin_permissions(collection_id, sender.clone())
1118 ),
1103 Error::<T>::NoPermission);1119 Error::<T>::NoPermission
1120 );
11041121
1158 // Transfer permissions check 1175 // Transfer permissions check
1159 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1176 ensure!(
1177 appoved_transfer ||
1178 (
1179 target_collection.limits.owner_can_transfer &&
1180 Self::is_owner_or_admin_permissions(collection_id, sender.clone())
1181 ),
1160 Error::<T>::NoPermission);1182 Error::<T>::NoPermission
1183 );
11611184
1524 pub fn set_collection_limits(1547 pub fn set_collection_limits(
1525 origin,1548 origin,
1526 collection_id: u32,1549 collection_id: u32,
1527 limits: CollectionLimits,1550 new_limits: CollectionLimits,
1528 ) -> DispatchResult {1551 ) -> DispatchResult {
1529 let sender = ensure_signed(origin)?;1552 let sender = ensure_signed(origin)?;
1530 Self::check_owner_permissions(collection_id, sender.clone())?;1553 Self::check_owner_permissions(collection_id, sender.clone())?;
1531 let mut target_collection = <Collection<T>>::get(collection_id);1554 let mut target_collection = <Collection<T>>::get(collection_id);
1555 let old_limits = target_collection.limits;
1532 let chain_limits = ChainLimit::get();1556 let chain_limits = ChainLimit::get();
1533 let climits = target_collection.limits;
15341557
1535 // collection bounds1558 // collection bounds
1536 ensure!(limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1559 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&
1537 limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP, 1560 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&
1561 new_limits.sponsored_data_size <= chain_limits.custom_data_limit &&
1562 new_limits.sponsored_mint_size <= chain_limits.custom_data_limit,
1538 Error::<T>::CollectionLimitBoundsExceeded);1563 Error::<T>::CollectionLimitBoundsExceeded);
15391564
1540 // token_limit check prev1565 // token_limit check prev
1541 ensure!(climits.token_limit > limits.token_limit && 1566 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);
1567 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);
1568
1569 ensure!(
1570 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&
1542 limits.token_limit <= chain_limits.account_token_ownership_limit, 1571 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),
1543 Error::<T>::AccountTokenLimitExceeded);1572 Error::<T>::OwnerPermissionsCantBeReverted,
1573 );
15441574
1545 target_collection.limits = limits;1575 target_collection.limits = new_limits;
1546 <Collection<T>>::insert(collection_id, target_collection);1576 <Collection<T>>::insert(collection_id, target_collection);
15471577
1548 Ok(())1578 Ok(())
modifiedtests/src/approve.test.tsdiffbeforeafterboth
3// file 'LICENSE', which is part of this source code package.3// file 'LICENSE', which is part of this source code package.
4//4//
5import { ApiPromise } from '@polkadot/api';5import { ApiPromise } from '@polkadot/api';
6import { IKeyringPair } from '@polkadot/types/types';
6import BN from 'bn.js';7import BN from 'bn.js';
7import chai from 'chai';8import chai from 'chai';
8import chaiAsPromised from 'chai-as-promised';9import chaiAsPromised from 'chai-as-promised';
15 createFungibleItemExpectSuccess,16 createFungibleItemExpectSuccess,
16 createItemExpectSuccess,17 createItemExpectSuccess,
17 destroyCollectionExpectSuccess,18 destroyCollectionExpectSuccess,
19 setCollectionLimitsExpectSuccess,
18 transferFromExpectSuccess,20 transferFromExpectSuccess,
19 U128_MAX,21 U128_MAX,
20} from './util/helpers';22} from './util/helpers';
23const expect = chai.expect;25const expect = chai.expect;
2426
25describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {27describe('Integration Test approve(spender, collection_id, item_id, amount):', () => {
28 let Alice: IKeyringPair;
29 let Bob: IKeyringPair;
30 let Charlie: IKeyringPair;
31
32 before(async () => {
33 await usingApi(async (api) => {
34 Alice = privateKey('//Alice');
35 Bob = privateKey('//Bob');
36 Charlie = privateKey('//Charlie');
37 });
38 });
39
26 it('Execute the extrinsic and check approvedList', async () => {40 it('Execute the extrinsic and check approvedList', async () => {
27 await usingApi(async (api: ApiPromise) => {41 await usingApi(async (api: ApiPromise) => {
28 const Alice = privateKey('//Alice');
29 const Bob = privateKey('//Bob');
30 const nftCollectionId = await createCollectionExpectSuccess();42 const nftCollectionId = await createCollectionExpectSuccess();
31 // nft43 // nft
32 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');44 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
4557
46 it('Remove approval by using 0 amount', async () => {58 it('Remove approval by using 0 amount', async () => {
47 await usingApi(async (api: ApiPromise) => {59 await usingApi(async (api: ApiPromise) => {
48 const Alice = privateKey('//Alice');
49 const Bob = privateKey('//Bob');
50 const nftCollectionId = await createCollectionExpectSuccess();60 const nftCollectionId = await createCollectionExpectSuccess();
51 // nft61 // nft
52 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');62 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
66 });76 });
67 });77 });
78
79 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);
82
83 await approveExpectSuccess(collectionId, itemId, Alice, Charlie);
84 });
68});85});
6986
70describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {87describe('Negative Integration Test approve(spender, collection_id, item_id, amount):', () => {
88 let Alice: IKeyringPair;
89 let Bob: IKeyringPair;
90 let Charlie: IKeyringPair;
91
92 before(async () => {
93 await usingApi(async (api) => {
94 Alice = privateKey('//Alice');
95 Bob = privateKey('//Bob');
96 Charlie = privateKey('//Charlie');
97 });
98 });
99
71 it('Approve for a collection that does not exist', async () => {100 it('Approve for a collection that does not exist', async () => {
72 await usingApi(async (api: ApiPromise) => {101 await usingApi(async (api: ApiPromise) => {
73 const Alice = privateKey('//Alice');
74 const Bob = privateKey('//Bob');
75 // nft102 // nft
76 const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;103 const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
77 await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);104 await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
86113
87 it('Approve for a collection that was destroyed', async () => {114 it('Approve for a collection that was destroyed', async () => {
88 await usingApi(async (api: ApiPromise) => {115 await usingApi(async (api: ApiPromise) => {
89 const Alice = privateKey('//Alice');
90 const Bob = privateKey('//Bob');
91 // nft116 // nft
92 const nftCollectionId = await createCollectionExpectSuccess();117 const nftCollectionId = await createCollectionExpectSuccess();
93 await destroyCollectionExpectSuccess(nftCollectionId);118 await destroyCollectionExpectSuccess(nftCollectionId);
106131
107 it('Approve transfer of a token that does not exist', async () => {132 it('Approve transfer of a token that does not exist', async () => {
108 await usingApi(async (api: ApiPromise) => {133 await usingApi(async (api: ApiPromise) => {
109 const Alice = privateKey('//Alice');
110 const Bob = privateKey('//Bob');
111 // nft134 // nft
112 const nftCollectionId = await createCollectionExpectSuccess();135 const nftCollectionId = await createCollectionExpectSuccess();
113 await approveExpectFail(nftCollectionId, 2, Alice, Bob);136 await approveExpectFail(nftCollectionId, 2, Alice, Bob);
123146
124 it('Approve using the address that does not own the approved token', async () => {147 it('Approve using the address that does not own the approved token', async () => {
125 await usingApi(async (api: ApiPromise) => {148 await usingApi(async (api: ApiPromise) => {
126 const Alice = privateKey('//Alice');
127 const Bob = privateKey('//Bob');
128 const nftCollectionId = await createCollectionExpectSuccess();149 const nftCollectionId = await createCollectionExpectSuccess();
129 // nft150 // nft
130 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');151 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
141 });162 });
142 });163 });
164
165 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 });
169
170 await approveExpectFail(collectionId, itemId, Alice, Charlie);
171 });
143});172});
144173
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
3// file 'LICENSE', which is part of this source code package.3// file 'LICENSE', which is part of this source code package.
4//4//
55
6import { IKeyringPair } from '@polkadot/types/types';
6import chai from 'chai';7import chai from 'chai';
7import chaiAsPromised from 'chai-as-promised';8import chaiAsPromised from 'chai-as-promised';
9import privateKey from './substrate/privateKey';
8import { default as usingApi } from "./substrate/substrate-api";10import { default as usingApi } from "./substrate/substrate-api";
9import { createCollectionExpectSuccess, destroyCollectionExpectSuccess, destroyCollectionExpectFailure } from "./util/helpers";11import { createCollectionExpectSuccess, destroyCollectionExpectSuccess, destroyCollectionExpectFailure, setCollectionLimitsExpectSuccess } from "./util/helpers";
1012
11chai.use(chaiAsPromised);13chai.use(chaiAsPromised);
1214
26});28});
2729
28describe('(!negative test!) integration test: ext. destroyCollection():', () => {30describe('(!negative test!) integration test: ext. destroyCollection():', () => {
31 let alice: IKeyringPair;
32
33 before(async () => {
34 await usingApi(async (api) => {
35 alice = privateKey('//Alice');
36 });
37 });
38
29 it('(!negative test!) Destroy a collection that never existed', async () => {39 it('(!negative test!) Destroy a collection that never existed', async () => {
30 await usingApi(async (api) => {40 await usingApi(async (api) => {
43 await destroyCollectionExpectFailure(collectionId, '//Bob');53 await destroyCollectionExpectFailure(collectionId, '//Bob');
44 await destroyCollectionExpectSuccess(collectionId, '//Alice');54 await destroyCollectionExpectSuccess(collectionId, '//Alice');
45 });55 });
56 it('fails when OwnerCanDestroy == false', async () => {
57 const collectionId = await createCollectionExpectSuccess();
58 await setCollectionLimitsExpectSuccess(alice, collectionId, { OwnerCanDestroy: false });
59
60 await destroyCollectionExpectFailure(collectionId, '//Alice');
61 });
46});62});
4763
modifiedtests/src/setCollectionLimits.test.tsdiffbeforeafterboth
14 createCollectionExpectSuccess, getCreatedCollectionCount,14 createCollectionExpectSuccess, getCreatedCollectionCount,
15 getCreateItemResult,15 getCreateItemResult,
16 getDetailedCollectionInfo,16 getDetailedCollectionInfo,
17 setCollectionLimitsExpectFailure,
18 setCollectionLimitsExpectSuccess,
17} from './util/helpers';19} from './util/helpers';
1820
19chai.use(chaiAsPromised);21chai.use(chaiAsPromised);
26const accountTokenOwnershipLimit = 0;28const accountTokenOwnershipLimit = 0;
27const sponsoredDataSize = 0;29const sponsoredDataSize = 0;
28const sponsoredMintSize = 0;30const sponsoredMintSize = 0;
29const tokenLimit = 0;31const sponsorTimeout = 1;
30
31describe('hooks', () => {
32 before(async () => {
33 await usingApi(async () => {
34 const keyring = new Keyring({ type: 'sr25519' });32const tokenLimit = 1;
35 alice = keyring.addFromUri('//Alice');
36 });
37 });
38 it('choose or create collection for testing', async () => {
39 await usingApi(async () => {
40 collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
41 });
42 });
43});
4433
45describe('setCollectionLimits positive', () => {34describe('setCollectionLimits positive', () => {
46 let tx;35 let tx;
47 before(async () => {36 before(async () => {
48 await usingApi(async () => {37 await usingApi(async () => {
49 const keyring = new Keyring({ type: 'sr25519' });38 const keyring = new Keyring({ type: 'sr25519' });
50 alice = keyring.addFromUri('//Alice');39 alice = keyring.addFromUri('//Alice');
40 collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
51 });41 });
52 });42 });
53 it('execute setCollectionLimits with predefined params ', async () => {
54 await usingApi(async (api: ApiPromise) => {
55 tx = api.tx.nft.setCollectionLimits(
56 collectionIdForTesting,
57 {
58 accountTokenOwnershipLimit,
59 sponsoredDataSize,
60 sponsoredMintSize,
61 tokenLimit,
62 },
63 );
64 const events = await submitTransactionAsync(alice, tx);
65 const result = getCreateItemResult(events);
66 // tslint:disable-next-line:no-unused-expression
67 expect(result.success).to.be.true;
68 });
69 });
70 it('get collection limits defined in previous test', async () => {43 it('execute setCollectionLimits with predefined params ', async () => {
71 await usingApi(async (api: ApiPromise) => {44 await usingApi(async (api: ApiPromise) => {
45 tx = api.tx.nft.setCollectionLimits(
46 collectionIdForTesting,
47 {
48 AccountTokenOwnershipLimit: accountTokenOwnershipLimit,
49 SponsoredMintSize: sponsoredDataSize,
50 TokenLimit: tokenLimit,
51 SponsorTimeout: sponsorTimeout,
52 OwnerCanTransfer: true,
53 OwnerCanDestroy: true
54 },
55 );
56 const events = await submitTransactionAsync(alice, tx);
57 const result = getCreateItemResult(events);
58
59 // get collection limits defined previously
72 const collectionInfo = await getDetailedCollectionInfo(api, collectionIdForTesting) as ICollectionInterface;60 const collectionInfo = await getDetailedCollectionInfo(api, collectionIdForTesting) as ICollectionInterface;
61
62 // tslint:disable-next-line:no-unused-expression
63 expect(result.success).to.be.true;
73 expect(collectionInfo.Limits.AccountTokenOwnershipLimit.toNumber()).to.be.equal(accountTokenOwnershipLimit);64 expect(collectionInfo.Limits.AccountTokenOwnershipLimit.toNumber()).to.be.equal(accountTokenOwnershipLimit);
74 expect(collectionInfo.Limits.SponsoredMintSize.toNumber()).to.be.equal(sponsoredMintSize);65 expect(collectionInfo.Limits.SponsoredMintSize.toNumber()).to.be.equal(sponsoredDataSize);
75 expect(collectionInfo.Limits.TokenLimit.toNumber()).to.be.equal(tokenLimit);66 expect(collectionInfo.Limits.TokenLimit.toNumber()).to.be.equal(tokenLimit);
76 expect(collectionInfo.Limits.SponsorTimeout.toNumber()).to.be.equal(sponsoredDataSize);67 expect(collectionInfo.Limits.SponsorTimeout.toNumber()).to.be.equal(sponsorTimeout);
68 expect(collectionInfo.Limits.OwnerCanTransfer.valueOf()).to.be.true;
69 expect(collectionInfo.Limits.OwnerCanDestroy.valueOf()).to.be.true;
77 });70 });
78 });71 });
79});72});
85 const keyring = new Keyring({ type: 'sr25519' });78 const keyring = new Keyring({ type: 'sr25519' });
86 alice = keyring.addFromUri('//Alice');79 alice = keyring.addFromUri('//Alice');
87 bob = keyring.addFromUri('//Bob');80 bob = keyring.addFromUri('//Bob');
81 collectionIdForTesting = await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});
88 });82 });
89 });83 });
90 it('execute setCollectionLimits for not exists collection', async () => {84 it('execute setCollectionLimits for not exists collection', async () => {
132 });126 });
133 });127 });
128
129 it('fails when trying to enable OwnerCanTransfer after it was disabled', async () => {
130 const collectionId = await createCollectionExpectSuccess();
131 await setCollectionLimitsExpectSuccess(alice, collectionId, {
132 AccountTokenOwnershipLimit: accountTokenOwnershipLimit,
133 SponsoredMintSize: sponsoredDataSize,
134 TokenLimit: tokenLimit,
135 SponsorTimeout: sponsorTimeout,
136 OwnerCanTransfer: false,
137 OwnerCanDestroy: true
138 });
139 await setCollectionLimitsExpectFailure(alice, collectionId, {
140 AccountTokenOwnershipLimit: accountTokenOwnershipLimit,
141 SponsoredMintSize: sponsoredDataSize,
142 TokenLimit: tokenLimit,
143 SponsorTimeout: sponsorTimeout,
144 OwnerCanTransfer: true,
145 OwnerCanDestroy: true
146 });
147 });
148
149 it('fails when trying to enable OwnerCanDestroy after it was disabled', async () => {
150 const collectionId = await createCollectionExpectSuccess();
151 await setCollectionLimitsExpectSuccess(alice, collectionId, {
152 AccountTokenOwnershipLimit: accountTokenOwnershipLimit,
153 SponsoredMintSize: sponsoredDataSize,
154 TokenLimit: tokenLimit,
155 SponsorTimeout: sponsorTimeout,
156 OwnerCanTransfer: true,
157 OwnerCanDestroy: false
158 });
159 await setCollectionLimitsExpectFailure(alice, collectionId, {
160 AccountTokenOwnershipLimit: accountTokenOwnershipLimit,
161 SponsoredMintSize: sponsoredDataSize,
162 TokenLimit: tokenLimit,
163 SponsorTimeout: sponsorTimeout,
164 OwnerCanTransfer: true,
165 OwnerCanDestroy: true
166 });
167 });
134});168});
135169
modifiedtests/src/transferFrom.test.tsdiffbeforeafterboth
3// file 'LICENSE', which is part of this source code package.3// file 'LICENSE', which is part of this source code package.
4//4//
5import { ApiPromise } from '@polkadot/api';5import { ApiPromise } from '@polkadot/api';
6import { IKeyringPair } from '@polkadot/types/types';
6import chai from 'chai';7import chai from 'chai';
7import chaiAsPromised from 'chai-as-promised';8import chaiAsPromised from 'chai-as-promised';
8import privateKey from './substrate/privateKey';9import privateKey from './substrate/privateKey';
18 transferFromExpectFail,19 transferFromExpectFail,
19 transferFromExpectSuccess,20 transferFromExpectSuccess,
20 burnItemExpectSuccess,21 burnItemExpectSuccess,
22 setCollectionLimitsExpectSuccess,
21} from './util/helpers';23} from './util/helpers';
2224
23chai.use(chaiAsPromised);25chai.use(chaiAsPromised);
24const expect = chai.expect;26const expect = chai.expect;
2527
26describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {28describe('Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
29 let Alice: IKeyringPair;
30 let Bob: IKeyringPair;
31 let Charlie: IKeyringPair;
32
33 before(async () => {
34 await usingApi(async (api) => {
35 Alice = privateKey('//Alice');
36 Bob = privateKey('//Bob');
37 Charlie = privateKey('//Charlie');
38 });
39 });
40
27 it('Execute the extrinsic and check nftItemList - owner of token', async () => {41 it('Execute the extrinsic and check nftItemList - owner of token', async () => {
28 await usingApi(async (api: ApiPromise) => {42 await usingApi(async (api: ApiPromise) => {
29 const Alice = privateKey('//Alice');
30 const Bob = privateKey('//Bob');
31 const Charlie = privateKey('//Charlie');
32 // nft43 // nft
33 const nftCollectionId = await createCollectionExpectSuccess();44 const nftCollectionId = await createCollectionExpectSuccess();
34 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');45 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
67 });78 });
68 });79 });
80
81 it('can be called by collection owner on non-owned item when OwnerCanTransfer == true', async () => {
82 const collectionId = await createCollectionExpectSuccess();
83 const itemId = await createItemExpectSuccess(Alice, collectionId, 'NFT', Bob.address);
84
85 await transferFromExpectSuccess(collectionId, itemId, Alice, Bob, Charlie);
86 });
69});87});
7088
71describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {89describe('Negative Integration Test transferFrom(from, recipient, collection_id, item_id, value):', () => {
90 let Alice: IKeyringPair;
91 let Bob: IKeyringPair;
92 let Charlie: IKeyringPair;
93
94 before(async () => {
95 await usingApi(async (api) => {
96 Alice = privateKey('//Alice');
97 Bob = privateKey('//Bob');
98 Charlie = privateKey('//Charlie');
99 });
100 });
101
72 it('transferFrom for a collection that does not exist', async () => {102 it('transferFrom for a collection that does not exist', async () => {
73 await usingApi(async (api: ApiPromise) => {103 await usingApi(async (api: ApiPromise) => {
74 const Alice = privateKey('//Alice');
75 const Bob = privateKey('//Bob');
76 const Charlie = privateKey('//Charlie');
77 // nft104 // nft
78 const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;105 const nftCollectionCount = await api.query.nft.createdCollectionCount() as unknown as number;
79 await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);106 await approveExpectFail(nftCollectionCount + 1, 1, Alice, Bob);
113140
114 it('transferFrom for not approved address', async () => {141 it('transferFrom for not approved address', async () => {
115 await usingApi(async (api: ApiPromise) => {142 await usingApi(async (api: ApiPromise) => {
116 const Alice = privateKey('//Alice');
117 const Bob = privateKey('//Bob');
118 const Charlie = privateKey('//Charlie');
119 // nft143 // nft
120 const nftCollectionId = await createCollectionExpectSuccess();144 const nftCollectionId = await createCollectionExpectSuccess();
121 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');145 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
137161
138 it('transferFrom incorrect token count', async () => {162 it('transferFrom incorrect token count', async () => {
139 await usingApi(async (api: ApiPromise) => {163 await usingApi(async (api: ApiPromise) => {
140 const Alice = privateKey('//Alice');
141 const Bob = privateKey('//Bob');
142 const Charlie = privateKey('//Charlie');
143 // nft164 // nft
144 const nftCollectionId = await createCollectionExpectSuccess();165 const nftCollectionId = await createCollectionExpectSuccess();
145 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');166 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
164185
165 it('execute transferFrom from account that is not owner of collection', async () => {186 it('execute transferFrom from account that is not owner of collection', async () => {
166 await usingApi(async (api: ApiPromise) => {187 await usingApi(async (api: ApiPromise) => {
167 const Alice = privateKey('//Alice');
168 const Bob = privateKey('//Bob');
169 const Charlie = privateKey('//Charlie');
170 const Dave = privateKey('//Dave');188 const Dave = privateKey('//Dave');
171 // nft189 // nft
172 const nftCollectionId = await createCollectionExpectSuccess();190 const nftCollectionId = await createCollectionExpectSuccess();
206 });224 });
207 it( 'transferFrom burnt token before approve NFT', async () => {225 it( 'transferFrom burnt token before approve NFT', async () => {
208 await usingApi(async (api: ApiPromise) => {226 await usingApi(async (api: ApiPromise) => {
209 const Alice = privateKey('//Alice');
210 const Bob = privateKey('//Bob');
211 const Charlie = privateKey('//Charlie');
212 // nft227 // nft
213 const nftCollectionId = await createCollectionExpectSuccess();228 const nftCollectionId = await createCollectionExpectSuccess();
214 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');229 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
219 });234 });
220 it( 'transferFrom burnt token before approve Fungible', async () => {235 it( 'transferFrom burnt token before approve Fungible', async () => {
221 await usingApi(async (api: ApiPromise) => {236 await usingApi(async (api: ApiPromise) => {
222 const Alice = privateKey('//Alice');
223 const Bob = privateKey('//Bob');
224 const Charlie = privateKey('//Charlie');
225 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});237 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
226 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');238 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
227 await burnItemExpectSuccess(Alice, fungibleCollectionId, 1, 10);239 await burnItemExpectSuccess(Alice, fungibleCollectionId, 1, 10);
232 }); 244 });
233 it( 'transferFrom burnt token before approve ReFungible', async () => {245 it( 'transferFrom burnt token before approve ReFungible', async () => {
234 await usingApi(async (api: ApiPromise) => {246 await usingApi(async (api: ApiPromise) => {
235 const Alice = privateKey('//Alice');
236 const Bob = privateKey('//Bob');
237 const Charlie = privateKey('//Charlie');
238 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});247 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
239 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');248 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
240 await burnItemExpectSuccess(Alice, reFungibleCollectionId, newReFungibleTokenId, 1);249 await burnItemExpectSuccess(Alice, reFungibleCollectionId, newReFungibleTokenId, 1);
246 255
247 it( 'transferFrom burnt token after approve NFT', async () => {256 it( 'transferFrom burnt token after approve NFT', async () => {
248 await usingApi(async (api: ApiPromise) => {257 await usingApi(async (api: ApiPromise) => {
249 const Alice = privateKey('//Alice');
250 const Bob = privateKey('//Bob');
251 const Charlie = privateKey('//Charlie');
252 // nft258 // nft
253 const nftCollectionId = await createCollectionExpectSuccess();259 const nftCollectionId = await createCollectionExpectSuccess();
254 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');260 const newNftTokenId = await createItemExpectSuccess(Alice, nftCollectionId, 'NFT');
259 });265 });
260 it( 'transferFrom burnt token after approve Fungible', async () => {266 it( 'transferFrom burnt token after approve Fungible', async () => {
261 await usingApi(async (api: ApiPromise) => {267 await usingApi(async (api: ApiPromise) => {
262 const Alice = privateKey('//Alice');
263 const Bob = privateKey('//Bob');
264 const Charlie = privateKey('//Charlie');
265 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});268 const fungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
266 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');269 const newFungibleTokenId = await createItemExpectSuccess(Alice, fungibleCollectionId, 'Fungible');
267 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);270 await approveExpectSuccess(fungibleCollectionId, newFungibleTokenId, Alice, Bob);
272 }); 275 });
273 it( 'transferFrom burnt token after approve ReFungible', async () => {276 it( 'transferFrom burnt token after approve ReFungible', async () => {
274 await usingApi(async (api: ApiPromise) => {277 await usingApi(async (api: ApiPromise) => {
275 const Alice = privateKey('//Alice');
276 const Bob = privateKey('//Bob');
277 const Charlie = privateKey('//Charlie');
278 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});278 const reFungibleCollectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
279 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');279 const newReFungibleTokenId = await createItemExpectSuccess(Alice, reFungibleCollectionId, 'ReFungible');
280 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);280 await approveExpectSuccess(reFungibleCollectionId, newReFungibleTokenId, Alice, Bob);
284 });284 });
285 }); 285 });
286
287 it('fails when called by collection owner on non-owned item when OwnerCanTransfer == false', async () => {
288 const collectionId = await createCollectionExpectSuccess();
289 const itemId = await createItemExpectSuccess(Alice, collectionId, 'NFT', Bob.address);
290 await setCollectionLimitsExpectSuccess(Alice, collectionId, { OwnerCanTransfer: false });
291
292 await transferFromExpectFail(collectionId, itemId, Alice, Bob, Charlie);
293 });
286});294});
287295
modifiedtests/src/types.tsdiffbeforeafterboth
17 SponsoredMintSize: BN;17 SponsoredMintSize: BN;
18 TokenLimit: BN;18 TokenLimit: BN;
19 SponsorTimeout: BN;19 SponsorTimeout: BN;
20 OwnerCanTransfer: boolean;
21 OwnerCanDestroy: boolean;
20 };22 };
21 MintMode: boolean;23 MintMode: boolean;
22 Mode: {24 Mode: {
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
333 });333 });
334}334}
335
336export async function queryCollectionLimits(collectionId: number) {
337 return await usingApi(async (api) => {
338 return ((await api.query.nft.collection(collectionId)).toJSON() as any).Limits;
339 });
340}
341
342export async function setCollectionLimitsExpectSuccess(sender: IKeyringPair, collectionId: number, limits: any) {
343 await usingApi(async (api) => {
344 const oldLimits = await queryCollectionLimits(collectionId);
345 const newLimits = { ...oldLimits as any, ...limits };
346 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);
347 const events = await submitTransactionAsync(sender, tx);
348 const result = getGenericResult(events);
349
350 expect(result.success).to.be.true;
351 });
352}
353
354export async function setCollectionLimitsExpectFailure(sender: IKeyringPair, collectionId: number, limits: any) {
355 await usingApi(async (api) => {
356 const oldLimits = await queryCollectionLimits(collectionId);
357 const newLimits = { ...oldLimits as any, ...limits };
358 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);
359 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;
360 const result = getGenericResult(events);
361
362 expect(result.success).to.be.false;
363 });
364}
335365
336export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {366export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {
337 await usingApi(async (api) => {367 await usingApi(async (api) => {