git.delta.rocks / unique-network / refs/commits / 0231a432ed7d

difftreelog

Merge pull request #596 from UniqueNetwork/test/playground-migration

ut-akuznetsov2022-09-30parents: #34b3ba3 #12fbdbd.patch.diff
in: master
Test/playground migration

15 files changed

modifiedtests/package.jsondiffbeforeafterboth
44 "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",44 "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",
45 "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",45 "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",
46 "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",46 "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",
47 "testChangeCollectionOwner": "mocha --timeout 9999999 -r ts-node/register ./**/change-collection-owner.test.ts",
47 "testSetCollectionSponsor": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionSponsor.test.ts",48 "testSetCollectionSponsor": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionSponsor.test.ts",
48 "testConfirmSponsorship": "mocha --timeout 9999999 -r ts-node/register ./**/confirmSponsorship.test.ts",49 "testConfirmSponsorship": "mocha --timeout 9999999 -r ts-node/register ./**/confirmSponsorship.test.ts",
49 "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",50 "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",
modifiedtests/src/adminTransferAndBurn.test.tsdiffbeforeafterboth
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import chai from 'chai';
19import chaiAsPromised from 'chai-as-promised';
20import {usingPlaygrounds} from './util/playgrounds';18import {usingPlaygrounds, expect, itSub} from './util/playgrounds';
21
22chai.use(chaiAsPromised);
23const expect = chai.expect;
24
25let donor: IKeyringPair;
2619
27describe('Integration Test: ownerCanTransfer allows admins to use only transferFrom/burnFrom:', () => {20describe('Integration Test: ownerCanTransfer allows admins to use only transferFrom/burnFrom:', () => {
28 let alice: IKeyringPair;21 let alice: IKeyringPair;
3124
32 before(async () => {25 before(async () => {
33 await usingPlaygrounds(async (helper, privateKey) => {26 await usingPlaygrounds(async (helper, privateKey) => {
34 donor = privateKey('//Alice');27 const donor = privateKey('//Alice');
35 [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);28 [alice, bob, charlie] = await helper.arrange.createAccounts([10n, 10n, 10n], donor);
36 });29 });
37 });30 });
3831
39 it('admin transfers other user\'s token', async () => {32 itSub('admin transfers other user\'s token', async ({helper}) => {
40 await usingPlaygrounds(async (helper) => {
41 const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});33 const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
42 await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});34 await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
43 const limits = await helper.collection.getEffectiveLimits(collectionId);35 const limits = await helper.collection.getEffectiveLimits(collectionId);
50 await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address});42 await helper.nft.transferTokenFrom(alice, collectionId, tokenId, {Substrate: bob.address}, {Substrate: charlie.address});
51 const newTokenOwner = await helper.nft.getTokenOwner(collectionId, tokenId);43 const newTokenOwner = await helper.nft.getTokenOwner(collectionId, tokenId);
52 expect(newTokenOwner.Substrate).to.be.equal(charlie.address);44 expect(newTokenOwner.Substrate).to.be.equal(charlie.address);
53 });
54 });45 });
5546
56 it('admin burns other user\'s token', async () => {47 itSub('admin burns other user\'s token', async ({helper}) => {
57 await usingPlaygrounds(async (helper) => {
58 const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});48 const {collectionId} = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
5949
60 await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});50 await helper.collection.setLimits(alice, collectionId, {ownerCanTransfer: true});
69 await helper.nft.burnToken(bob, collectionId, tokenId);59 await helper.nft.burnToken(bob, collectionId, tokenId);
70 const token = await helper.nft.getToken(collectionId, tokenId);60 const token = await helper.nft.getToken(collectionId, tokenId);
71 expect(token).to.be.null;61 expect(token).to.be.null;
72 });
73 });62 });
74});63});
7564
modifiedtests/src/allowLists.test.tsdiffbeforeafterboth
18import {usingPlaygrounds, expect, itSub} from './util/playgrounds';18import {usingPlaygrounds, expect, itSub} from './util/playgrounds';
19import {ICollectionPermissions} from './util/playgrounds/types';19import {ICollectionPermissions} from './util/playgrounds/types';
2020
21describe('Integration Test ext. Add to Allow List', () => { 21describe('Integration Test ext. Allow list tests', () => {
22 let alice: IKeyringPair;22 let alice: IKeyringPair;
23 let bob: IKeyringPair;23 let bob: IKeyringPair;
24 let charlie: IKeyringPair;24 let charlie: IKeyringPair;
modifiedtests/src/change-collection-owner.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import chai from 'chai';
18import chaiAsPromised from 'chai-as-promised';
19import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';17import {IKeyringPair} from '@polkadot/types/types';
20import {createCollectionExpectSuccess,18import {usingPlaygrounds, expect, itSub} from './util/playgrounds';
21 addCollectionAdminExpectSuccess,
22 setCollectionSponsorExpectSuccess,
23 confirmSponsorshipExpectSuccess,
24 removeCollectionSponsorExpectSuccess,
25 enableAllowListExpectSuccess,
26 setMintPermissionExpectSuccess,
27 destroyCollectionExpectSuccess,
28 setCollectionSponsorExpectFailure,
29 confirmSponsorshipExpectFailure,
30 removeCollectionSponsorExpectFailure,
31 enableAllowListExpectFail,
32 setMintPermissionExpectFailure,
33 destroyCollectionExpectFailure,
34 setPublicAccessModeExpectSuccess,
35 queryCollectionExpectSuccess,
36} from './util/helpers';
37
38chai.use(chaiAsPromised);
39const expect = chai.expect;
4019
41describe('Integration Test changeCollectionOwner(collection_id, new_owner):', () => {20describe('Integration Test changeCollectionOwner(collection_id, new_owner):', () => {
21 let alice: IKeyringPair;
22 let bob: IKeyringPair;
23
24 before(async () => {
25 await usingPlaygrounds(async (helper, privateKey) => {
26 const donor = privateKey('//Alice');
27 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
28 });
29 });
30
42 it('Changing owner changes owner address', async () => {31 itSub('Changing owner changes owner address', async ({helper}) => {
43 await usingApi(async (api, privateKeyWrapper) => {32 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
33 const beforeChanging = await helper.collection.getData(collection.collectionId);
44 const collectionId = await createCollectionExpectSuccess();34 expect(beforeChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
45 const alice = privateKeyWrapper('//Alice');
46 const bob = privateKeyWrapper('//Bob');
47
48 const collection =await queryCollectionExpectSuccess(api, collectionId);
49 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
5035
51 const changeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, bob.address);36 await collection.changeOwner(alice, bob.address);
52 await submitTransactionAsync(alice, changeOwnerTx);
53
54 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);37 const afterChanging = await helper.collection.getData(collection.collectionId);
55 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);38 expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
56 });
57 });39 });
58});40});
5941
60describe('Integration Test changeCollectionOwner(collection_id, new_owner) special checks for exOwner:', () => {42describe('Integration Test changeCollectionOwner(collection_id, new_owner) special checks for exOwner:', () => {
43 let alice: IKeyringPair;
44 let bob: IKeyringPair;
45 let charlie: IKeyringPair;
46
47 before(async () => {
48 await usingPlaygrounds(async (helper, privateKey) => {
49 const donor = privateKey('//Alice');
50 [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
51 });
52 });
53
61 it('Changing the owner of the collection is not allowed for the former owner', async () => {54 itSub('Changing the owner of the collection is not allowed for the former owner', async ({helper}) => {
62 await usingApi(async (api, privateKeyWrapper) => {55 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
56
63 const collectionId = await createCollectionExpectSuccess();57 await collection.changeOwner(alice, bob.address);
64 const alice = privateKeyWrapper('//Alice');
65 const bob = privateKeyWrapper('//Bob');
66
67 const collection = await queryCollectionExpectSuccess(api, collectionId);
68 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
69
70 const changeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, bob.address);
71 await submitTransactionAsync(alice, changeOwnerTx);
7258
73 const badChangeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, alice.address);59 const changeOwnerTx = async () => collection.changeOwner(alice, alice.address);
74 await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;60 await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
7561
76 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);62 const afterChanging = await helper.collection.getData(collection.collectionId);
77 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);63 expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
78 });
79 });64 });
8065
81 it('New collectionOwner has access to sponsorship management operations in the collection', async () => {66 itSub('New collectionOwner has access to sponsorship management operations in the collection', async ({helper}) => {
82 await usingApi(async (api, privateKeyWrapper) => {67 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
83 const collectionId = await createCollectionExpectSuccess();68 await collection.changeOwner(alice, bob.address);
84 const alice = privateKeyWrapper('//Alice');
85 const bob = privateKeyWrapper('//Bob');
86 const charlie = privateKeyWrapper('//Charlie');
87
88 const collection = await queryCollectionExpectSuccess(api, collectionId);
89 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
9069
91 const changeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, bob.address);70 const afterChanging = await helper.collection.getData(collection.collectionId);
92 await submitTransactionAsync(alice, changeOwnerTx);
93
94 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
95 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);71 expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
9672
97 // After changing the owner of the collection, all privileged methods are available to the new owner
98 // The new owner of the collection has access to sponsorship management operations in the collection
99 await setCollectionSponsorExpectSuccess(collectionId, charlie.address, '//Bob');73 await collection.setSponsor(bob, charlie.address);
100 await confirmSponsorshipExpectSuccess(collectionId, '//Charlie');74 await collection.confirmSponsorship(charlie);
101 await removeCollectionSponsorExpectSuccess(collectionId, '//Bob');75 await collection.removeSponsor(bob);
102
103 // The new owner of the collection has access to operations for managing the collection parameters
104 const collectionLimits = {76 const limits = {
105 accountTokenOwnershipLimit: 1,77 accountTokenOwnershipLimit: 1,
106 sponsoredMintSize: 1,
107 tokenLimit: 1,78 tokenLimit: 1,
108 sponsorTransferTimeout: 1,79 sponsorTransferTimeout: 1,
109 ownerCanTransfer: true,80 ownerCanDestroy: true,
110 ownerCanDestroy: true,81 ownerCanTransfer: true,
111 };82 };
83
112 const tx1 = api.tx.unique.setCollectionLimits(84 await collection.setLimits(bob, limits);
113 collectionId,
114 collectionLimits,
115 );
116 await submitTransactionAsync(bob, tx1);85 const gotLimits = await collection.getEffectiveLimits();
86 expect(gotLimits).to.be.deep.contains(limits);
11787
118 await setPublicAccessModeExpectSuccess(bob, collectionId, 'AllowList');88 await collection.setPermissions(bob, {access: 'AllowList', mintMode: true});
89
119 await enableAllowListExpectSuccess(bob, collectionId);90 await collection.burn(bob);
120 await setMintPermissionExpectSuccess(bob, collectionId, true);91 const collectionData = await helper.collection.getData(collection.collectionId);
121 await destroyCollectionExpectSuccess(collectionId, '//Bob');92 expect(collectionData).to.be.null;
122 });
123 });93 });
12494
125 it('New collectionOwner has access to changeCollectionOwner', async () => {95 itSub('New collectionOwner has access to changeCollectionOwner', async ({helper}) => {
126 await usingApi(async (api, privateKeyWrapper) => {96 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
127 const collectionId = await createCollectionExpectSuccess();97 await collection.changeOwner(alice, bob.address);
128 const alice = privateKeyWrapper('//Alice');
129 const bob = privateKeyWrapper('//Bob');
130 const charlie = privateKeyWrapper('//Charlie');
131
132 const collection = await queryCollectionExpectSuccess(api, collectionId);
133 expect(collection.owner.toString()).to.be.deep.eq(alice.address);
134
135 const changeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, bob.address);
136 await submitTransactionAsync(alice, changeOwnerTx);
137
138 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
139 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);
140
141 const changeOwnerTx2 = api.tx.unique.changeCollectionOwner(collectionId, charlie.address);98 await collection.changeOwner(bob, charlie.address);
142 await submitTransactionAsync(bob, changeOwnerTx2);
143
144 // ownership lost
145 const collectionAfterOwnerChange2 = await queryCollectionExpectSuccess(api, collectionId);99 const collectionData = await collection.getData();
146 expect(collectionAfterOwnerChange2.owner.toString()).to.be.deep.eq(charlie.address);100 expect(collectionData?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(charlie.address));
147 });
148 });101 });
149});102});
150103
151describe('Negative Integration Test changeCollectionOwner(collection_id, new_owner):', () => {104describe('Negative Integration Test changeCollectionOwner(collection_id, new_owner):', () => {
105 let alice: IKeyringPair;
106 let bob: IKeyringPair;
107 let charlie: IKeyringPair;
108
152 it('Not owner can\'t change owner.', async () => {109 before(async () => {
153 await usingApi(async (api, privateKeyWrapper) => {110 await usingPlaygrounds(async (helper, privateKey) => {
154 const collectionId = await createCollectionExpectSuccess();
155 const alice = privateKeyWrapper('//Alice');111 const donor = privateKey('//Alice');
156 const bob = privateKeyWrapper('//Bob');
157
158 const changeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, bob.address);112 [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
159 await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected;
160
161 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
162 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(alice.address);
163
164 // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
165 await createCollectionExpectSuccess();
166 });113 });
167 });114 });
115
116 itSub('Not owner can\'t change owner.', async ({helper}) => {
117 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
118 const changeOwnerTx = async () => collection.changeOwner(bob, bob.address);
119 await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
120 });
168121
169 it('Collection admin can\'t change owner.', async () => {122 itSub('Collection admin can\'t change owner.', async ({helper}) => {
170 await usingApi(async (api, privateKeyWrapper) => {123 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
171 const collectionId = await createCollectionExpectSuccess();124 await collection.addAdmin(alice, {Substrate: bob.address});
172 const alice = privateKeyWrapper('//Alice');
173 const bob = privateKeyWrapper('//Bob');
174
175 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
176
177 const changeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, bob.address);125 const changeOwnerTx = async () => collection.changeOwner(bob, bob.address);
178 await expect(submitTransactionExpectFailAsync(bob, changeOwnerTx)).to.be.rejected;126 await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
179
180 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
181 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(alice.address);
182
183 // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
184 await createCollectionExpectSuccess();
185 });
186 });127 });
187128
188 it('Can\'t change owner of a non-existing collection.', async () => {129 itSub('Can\'t change owner of a non-existing collection.', async ({helper}) => {
189 await usingApi(async (api, privateKeyWrapper) => {
190 const collectionId = (1<<32) - 1;130 const collectionId = (1 << 32) - 1;
191 const alice = privateKeyWrapper('//Alice');
192 const bob = privateKeyWrapper('//Bob');
193
194 const changeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, bob.address);131 const changeOwnerTx = async () => helper.collection.changeOwner(bob, collectionId, bob.address);
195 await expect(submitTransactionExpectFailAsync(alice, changeOwnerTx)).to.be.rejected;132 await expect(changeOwnerTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
196
197 // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
198 await createCollectionExpectSuccess();
199 });
200 });133 });
201134
202 it('Former collectionOwner not allowed to sponsorship management operations in the collection', async () => {135 itSub('Former collectionOwner not allowed to sponsorship management operations in the collection', async ({helper}) => {
203 await usingApi(async (api, privateKeyWrapper) => {136 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
204 const collectionId = await createCollectionExpectSuccess();137 await collection.changeOwner(alice, bob.address);
138
205 const alice = privateKeyWrapper('//Alice');139 const changeOwnerTx = async () => collection.changeOwner(alice, alice.address);
206 const bob = privateKeyWrapper('//Bob');140 await expect(changeOwnerTx()).to.be.rejectedWith(/common\.NoPermission/);
207 const charlie = privateKeyWrapper('//Charlie');
208141
209 const collection = await queryCollectionExpectSuccess(api, collectionId);142 const afterChanging = await helper.collection.getData(collection.collectionId);
210 expect(collection.owner.toString()).to.be.deep.eq(alice.address);143 expect(afterChanging?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(bob.address));
211144
212 const changeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, bob.address);145 const setSponsorTx = async () => collection.setSponsor(alice, charlie.address);
213 await submitTransactionAsync(alice, changeOwnerTx);146 const confirmSponsorshipTx = async () => collection.confirmSponsorship(alice);
214
215 const badChangeOwnerTx = api.tx.unique.changeCollectionOwner(collectionId, alice.address);147 const removeSponsorTx = async () => collection.removeSponsor(alice);
216 await expect(submitTransactionExpectFailAsync(alice, badChangeOwnerTx)).to.be.rejected;148 await expect(setSponsorTx()).to.be.rejectedWith(/common\.NoPermission/);
217
218 const collectionAfterOwnerChange = await queryCollectionExpectSuccess(api, collectionId);
219 expect(collectionAfterOwnerChange.owner.toString()).to.be.deep.eq(bob.address);149 await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
220
221 await setCollectionSponsorExpectFailure(collectionId, charlie.address, '//Alice');150 await expect(removeSponsorTx()).to.be.rejectedWith(/common\.NoPermission/);
222 await confirmSponsorshipExpectFailure(collectionId, '//Alice');
223 await removeCollectionSponsorExpectFailure(collectionId, '//Alice');
224151
225 const collectionLimits = {152 const limits = {
226 accountTokenOwnershipLimit: 1,153 accountTokenOwnershipLimit: 1,
227 sponsoredMintSize: 1,
228 tokenLimit: 1,154 tokenLimit: 1,
229 sponsorTransferTimeout: 1,155 sponsorTransferTimeout: 1,
230 ownerCanTransfer: true,156 ownerCanDestroy: true,
231 ownerCanDestroy: true,157 ownerCanTransfer: true,
232 };158 };
159
233 const tx1 = api.tx.unique.setCollectionLimits(160 const setLimitsTx = async () => collection.setLimits(alice, limits);
234 collectionId,
235 collectionLimits,
236 );
237 await expect(submitTransactionExpectFailAsync(alice, tx1)).to.be.rejected;161 await expect(setLimitsTx()).to.be.rejectedWith(/common\.NoPermission/);
238162
239 await enableAllowListExpectFail(alice, collectionId);163 const setPermissionTx = async () => collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
240 await setMintPermissionExpectFailure(alice, collectionId, true);164 await expect(setPermissionTx()).to.be.rejectedWith(/common\.NoPermission/);
165
166 const burnTx = async () => collection.burn(alice);
241 await destroyCollectionExpectFailure(collectionId, '//Alice');167 await expect(burnTx()).to.be.rejectedWith(/common\.NoPermission/);
242 });
243 });168 });
244});169});
245170
modifiedtests/src/confirmSponsorship.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import chai from 'chai';
18import chaiAsPromised from 'chai-as-promised';
19import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';
20import {
21 createCollectionExpectSuccess,
22 setCollectionSponsorExpectSuccess,
23 destroyCollectionExpectSuccess,
24 confirmSponsorshipExpectSuccess,
25 confirmSponsorshipExpectFailure,
26 createItemExpectSuccess,
27 findUnusedAddress,
28 getGenericResult,
29 enableAllowListExpectSuccess,
30 enablePublicMintingExpectSuccess,
31 addToAllowListExpectSuccess,
32 normalizeAccountId,
33 addCollectionAdminExpectSuccess,
34 getCreatedCollectionCount,
35 UNIQUE,
36 requirePallets,
37 Pallets,
38} from './util/helpers';
39import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import {usingPlaygrounds, expect, itSub, Pallets} from './util/playgrounds';
4019
20async function setSponsorHelper(collection: any, signer: IKeyringPair, sponsorAddress: string) {
41chai.use(chaiAsPromised);21 await collection.setSponsor(signer, sponsorAddress);
42const expect = chai.expect;22 const raw = (await collection.getData())?.raw;
23 expect(raw.sponsorship.Unconfirmed).to.be.equal(sponsorAddress);
24}
4325
44let alice: IKeyringPair;26async function confirmSponsorHelper(collection: any, signer: IKeyringPair) {
27 await collection.confirmSponsorship(signer);
45let bob: IKeyringPair;28 const raw = (await collection.getData())?.raw;
46let charlie: IKeyringPair;29 expect(raw.sponsorship.Confirmed).to.be.equal(signer.address);
30}
4731
48describe('integration test: ext. confirmSponsorship():', () => {32describe('integration test: ext. confirmSponsorship():', () => {
33 let alice: IKeyringPair;
34 let bob: IKeyringPair;
35 let charlie: IKeyringPair;
36 let zeroBalance: IKeyringPair;
4937
50 before(async () => {38 before(async () => {
51 await usingApi(async (api, privateKeyWrapper) => {39 await usingPlaygrounds(async (helper, privateKey) => {
52 alice = privateKeyWrapper('//Alice');40 const donor = privateKey('//Alice');
53 bob = privateKeyWrapper('//Bob');41 [alice, bob, charlie, zeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n], donor);
54 charlie = privateKeyWrapper('//Charlie');
55 });42 });
56 });43 });
5744
58 it('Confirm collection sponsorship', async () => {45 itSub('Confirm collection sponsorship', async ({helper}) => {
59 const collectionId = await createCollectionExpectSuccess();46 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
60 await setCollectionSponsorExpectSuccess(collectionId, bob.address);47 await setSponsorHelper(collection, alice, bob.address);
61 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');48 await confirmSponsorHelper(collection, bob);
62 });49 });
50
63 it('Add sponsor to a collection after the same sponsor was already added and confirmed', async () => {51 itSub('Add sponsor to a collection after the same sponsor was already added and confirmed', async ({helper}) => {
64 const collectionId = await createCollectionExpectSuccess();52 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
65 await setCollectionSponsorExpectSuccess(collectionId, bob.address);53 await setSponsorHelper(collection, alice, bob.address);
66 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');54 await confirmSponsorHelper(collection, bob);
67 await setCollectionSponsorExpectSuccess(collectionId, bob.address);55 await setSponsorHelper(collection, alice, bob.address);
68 });56 });
69 it('Add new sponsor to a collection after another sponsor was already added and confirmed', async () => {57 itSub('Add new sponsor to a collection after another sponsor was already added and confirmed', async ({helper}) => {
70 const collectionId = await createCollectionExpectSuccess();58 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
71 await setCollectionSponsorExpectSuccess(collectionId, bob.address);59 await setSponsorHelper(collection, alice, bob.address);
72 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');60 await confirmSponsorHelper(collection, bob);
73 await setCollectionSponsorExpectSuccess(collectionId, charlie.address);61 await setSponsorHelper(collection, alice, charlie.address);
74 });62 });
7563
76 it('NFT: Transfer fees are paid by the sponsor after confirmation', async () => {64 itSub('NFT: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => {
77 const collectionId = await createCollectionExpectSuccess();65 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
78 await setCollectionSponsorExpectSuccess(collectionId, bob.address);
79 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');66 await collection.setSponsor(alice, bob.address);
80
81 await usingApi(async (api, privateKeyWrapper) => {67 await collection.confirmSponsorship(bob);
82 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();68 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
83
84 // Find unused address69 const token = await collection.mintToken(alice, {Substrate: zeroBalance.address});
85 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
86
87 // Mint token for unused address
88 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', zeroBalance.address);
89
90 // Transfer this tokens from unused address to Alice70 await token.transfer(zeroBalance, {Substrate: alice.address});
91 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);
92 const events = await submitTransactionAsync(zeroBalance, zeroToAlice);
93 const result = getGenericResult(events);
94 expect(result.success).to.be.true;
95
96 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();71 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
97
98 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;72 expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
99 });
100
101 });73 });
10274
103 it('Fungible: Transfer fees are paid by the sponsor after confirmation', async () => {75 itSub('Fungible: Transfer fees are paid by the sponsor after confirmation', async ({helper}) => {
104 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});76 const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
105 await setCollectionSponsorExpectSuccess(collectionId, bob.address);77 await collection.setSponsor(alice, bob.address);
106 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');78 await collection.confirmSponsorship(bob);
107
108 await usingApi(async (api, privateKeyWrapper) => {79 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
109 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
110
111 // Find unused address80 await collection.mint(alice, 100n, {Substrate: zeroBalance.address});
112 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
113
114 // Mint token for unused address
115 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);
116
117 // Transfer this tokens from unused address to Alice81 await collection.transfer(zeroBalance, {Substrate: alice.address}, 1n);
118 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);
119 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);82 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
120 const result1 = getGenericResult(events1);
121 expect(result1.success).to.be.true;
122
123 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
124
125 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;83 expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
126 });
127 });84 });
12885
129 it('ReFungible: Transfer fees are paid by the sponsor after confirmation', async function() {86 itSub.ifWithPallets('ReFungible: Transfer fees are paid by the sponsor after confirmation', [Pallets.ReFungible], async ({helper}) => {
130 await requirePallets(this, [Pallets.ReFungible]);87 const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
131
132 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
133 await setCollectionSponsorExpectSuccess(collectionId, bob.address);88 await collection.setSponsor(alice, bob.address);
134 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');89 await collection.confirmSponsorship(bob);
135
136 await usingApi(async (api, privateKeyWrapper) => {90 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
137 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
138
139 // Find unused address91 const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address});
140 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
141
142 // Mint token for unused address
143 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);
144
145 // Transfer this tokens from unused address to Alice92 await token.transfer(zeroBalance, {Substrate: alice.address}, 1n);
146 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);
147 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);93 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
148 const result1 = getGenericResult(events1);
149
150 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
151
152 expect(result1.success).to.be.true;94 expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
153 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
154 });
155 });95 });
15696
157 it('CreateItem fees are paid by the sponsor after confirmation', async () => {97 itSub('CreateItem fees are paid by the sponsor after confirmation', async ({helper}) => {
158 const collectionId = await createCollectionExpectSuccess();98 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
159 await setCollectionSponsorExpectSuccess(collectionId, bob.address);99 await collection.setSponsor(alice, bob.address);
160 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');100 await collection.confirmSponsorship(bob);
101 await collection.setPermissions(alice, {access: 'AllowList', mintMode: true});
102 await collection.addToAllowList(alice, {Substrate: zeroBalance.address});
161103
162 // Enable collection allow list104 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
163 await enableAllowListExpectSuccess(alice, collectionId);105 await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
106 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
164107
165 // Enable public minting108 expect(bobBalanceAfter < bobBalanceBefore).to.be.true;
166 await enablePublicMintingExpectSuccess(alice, collectionId);
167
168 // Create Item
169 await usingApi(async (api, privateKeyWrapper) => {
170 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
171
172 // Find unused address
173 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
174
175 // Add zeroBalance address to allow list
176 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
177
178 // Mint token using unused address as signer
179 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
180
181 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
182
183 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
184 });
185 });109 });
186110
187 it('NFT: Sponsoring of transfers is rate limited', async () => {111 itSub('NFT: Sponsoring of transfers is rate limited', async ({helper}) => {
188 const collectionId = await createCollectionExpectSuccess();112 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
189 await setCollectionSponsorExpectSuccess(collectionId, bob.address);113 await collection.setSponsor(alice, bob.address);
190 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');114 await collection.confirmSponsorship(bob);
191115
192 await usingApi(async (api, privateKeyWrapper) => {116 const token = await collection.mintToken(alice, {Substrate: alice.address});
193 // Find unused address117 await token.transfer(alice, {Substrate: zeroBalance.address});
194 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);118 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
195119
196 // Mint token for alice120 const transferTx = async () => token.transfer(zeroBalance, {Substrate: alice.address});
197 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', alice.address);121 await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
122 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
198123
199 // Transfer this token from Alice to unused address and back124 expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
200 // Alice to Zero gets sponsored
201 const aliceToZero = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 0);
202 const events1 = await submitTransactionAsync(alice, aliceToZero);
203 const result1 = getGenericResult(events1);
204
205 // Second transfer should fail
206 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
207 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);
208 const badTransaction = async function () {
209 await submitTransactionExpectFailAsync(zeroBalance, zeroToAlice);
210 };
211 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');
212 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
213
214 // Try again after Zero gets some balance - now it should succeed
215 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
216 await submitTransactionAsync(alice, balancetx);
217 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);
218 const result2 = getGenericResult(events2);
219
220 expect(result1.success).to.be.true;
221 expect(result2.success).to.be.true;
222 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
223 });
224 });125 });
225126
226 it('Fungible: Sponsoring is rate limited', async () => {127 itSub('Fungible: Sponsoring is rate limited', async ({helper}) => {
227 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});128 const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
228 await setCollectionSponsorExpectSuccess(collectionId, bob.address);129 await collection.setSponsor(alice, bob.address);
229 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');130 await collection.confirmSponsorship(bob);
230131
231 await usingApi(async (api, privateKeyWrapper) => {132 await collection.mint(alice, 100n, {Substrate: zeroBalance.address});
232 // Find unused address133 await collection.transfer(zeroBalance, {Substrate: zeroBalance.address}, 1n);
233 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);134 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
234135
235 // Mint token for unused address136 const transferTx = async () => collection.transfer(zeroBalance, {Substrate: zeroBalance.address});
236 const itemId = await createItemExpectSuccess(alice, collectionId, 'Fungible', zeroBalance.address);137 await expect(transferTx()).to.be.rejected;
138 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
237139
238 // Transfer this tokens in parts from unused address to Alice140 expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
239 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(zeroBalance.address), collectionId, itemId, 1);
240 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);
241 const result1 = getGenericResult(events1);
242 expect(result1.success).to.be.true;
243
244 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
245 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejected;
246 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
247
248 // Try again after Zero gets some balance - now it should succeed
249 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
250 await submitTransactionAsync(alice, balancetx);
251 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);
252 const result2 = getGenericResult(events2);
253 expect(result2.success).to.be.true;
254
255 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
256 });
257 });141 });
258142
259 it('ReFungible: Sponsoring is rate limited', async function() {143 itSub.ifWithPallets('ReFungible: Sponsoring is rate limited', [Pallets.ReFungible], async ({helper}) => {
260 await requirePallets(this, [Pallets.ReFungible]);144 const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
145 await collection.setSponsor(alice, bob.address);
146 await collection.confirmSponsorship(bob);
261147
262 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});148 const token = await collection.mintToken(alice, 100n, {Substrate: zeroBalance.address});
263 await setCollectionSponsorExpectSuccess(collectionId, bob.address);149 await token.transfer(zeroBalance, {Substrate: alice.address});
264 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
265150
266 await usingApi(async (api, privateKeyWrapper) => {151 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
152 const transferTx = async () => token.transfer(zeroBalance, {Substrate: alice.address});
267 // Find unused address153 await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
268 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);154 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
269155
270 // Mint token for alice156 expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
271 const itemId = await createItemExpectSuccess(alice, collectionId, 'ReFungible', zeroBalance.address);
272
273 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 1);
274
275 // Zero to alice gets sponsored
276 const events1 = await submitTransactionAsync(zeroBalance, zeroToAlice);
277 const result1 = getGenericResult(events1);
278 expect(result1.success).to.be.true;
279
280 // Second transfer should fail
281 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
282 await expect(submitTransactionExpectFailAsync(zeroBalance, zeroToAlice)).to.be.rejectedWith('Inability to pay some fees');
283 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
284 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
285
286 // Try again after Zero gets some balance - now it should succeed
287 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
288 await submitTransactionAsync(alice, balancetx);
289 const events2 = await submitTransactionAsync(zeroBalance, zeroToAlice);
290 const result2 = getGenericResult(events2);
291 expect(result2.success).to.be.true;
292 });
293 });157 });
294158
295 it('NFT: Sponsoring of createItem is rate limited', async () => {159 itSub('NFT: Sponsoring of createItem is rate limited', async ({helper}) => {
296 const collectionId = await createCollectionExpectSuccess();160 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
297 await setCollectionSponsorExpectSuccess(collectionId, bob.address);161 await collection.setSponsor(alice, bob.address);
298 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');162 await collection.confirmSponsorship(bob);
163 await collection.setPermissions(alice, {mintMode: true, access: 'AllowList'});
164 await collection.addToAllowList(alice, {Substrate: zeroBalance.address});
299165
300 // Enable collection allow list166 await collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
301 await enableAllowListExpectSuccess(alice, collectionId);
302167
303 // Enable public minting168 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
304 await enablePublicMintingExpectSuccess(alice, collectionId);169 const mintTx = async () => collection.mintToken(zeroBalance, {Substrate: zeroBalance.address});
170 await expect(mintTx()).to.be.rejectedWith('Inability to pay some fees');
171 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
305172
306 await usingApi(async (api, privateKeyWrapper) => {173 expect(bobBalanceAfter === bobBalanceBefore).to.be.true;
307 // Find unused address
308 const zeroBalance = await findUnusedAddress(api, privateKeyWrapper);
309
310 // Add zeroBalance address to allow list
311 await addToAllowListExpectSuccess(alice, collectionId, zeroBalance.address);
312
313 // Mint token using unused address as signer - gets sponsored
314 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
315
316 // Second mint should fail
317 const sponsorBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();
318
319 const badTransaction = async function () {
320 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
321 };
322 await expect(badTransaction()).to.be.rejectedWith('Inability to pay some fees');
323 const sponsorBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();
324
325 // Try again after Zero gets some balance - now it should succeed
326 const balancetx = api.tx.balances.transfer(zeroBalance.address, 1n * UNIQUE);
327 await submitTransactionAsync(alice, balancetx);
328 await createItemExpectSuccess(zeroBalance, collectionId, 'NFT', zeroBalance.address);
329
330 expect(sponsorBalanceAfter).to.be.equal(sponsorBalanceBefore);
331 });
332 });174 });
333
334});175});
335176
336describe('(!negative test!) integration test: ext. confirmSponsorship():', () => {177describe('(!negative test!) integration test: ext. confirmSponsorship():', () => {
178 let alice: IKeyringPair;
179 let bob: IKeyringPair;
180 let charlie: IKeyringPair;
181 let ownerZeroBalance: IKeyringPair;
182 let senderZeroBalance: IKeyringPair;
183
337 before(async () => {184 before(async () => {
338 await usingApi(async (api, privateKeyWrapper) => {185 await usingPlaygrounds(async (helper, privateKey) => {
339 alice = privateKeyWrapper('//Alice');186 const donor = privateKey('//Alice');
340 bob = privateKeyWrapper('//Bob');187 [alice, bob, charlie, ownerZeroBalance, senderZeroBalance] = await helper.arrange.createAccounts([100n, 100n, 100n, 0n, 0n], donor);
341 charlie = privateKeyWrapper('//Charlie');
342 });188 });
343 });189 });
344190
345 it('(!negative test!) Confirm sponsorship for a collection that never existed', async () => {191 itSub('(!negative test!) Confirm sponsorship for a collection that never existed', async ({helper}) => {
346 // Find the collection that never existed192 const collectionId = 1_000_000;
347 let collectionId = 0;
348 await usingApi(async (api) => {193 const confirmSponsorshipTx = async () => helper.collection.confirmSponsorship(bob, collectionId);
349 collectionId = await getCreatedCollectionCount(api) + 1;
350 });194 await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
351
352 await confirmSponsorshipExpectFailure(collectionId, '//Bob');
353 });195 });
354196
355 it('(!negative test!) Confirm sponsorship using a non-sponsor address', async () => {197 itSub('(!negative test!) Confirm sponsorship using a non-sponsor address', async ({helper}) => {
356 const collectionId = await createCollectionExpectSuccess();198 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
357 await setCollectionSponsorExpectSuccess(collectionId, bob.address);199 await collection.setSponsor(alice, bob.address);
358
359 await usingApi(async (api) => {200 const confirmSponsorshipTx = async () => collection.confirmSponsorship(charlie);
360 const transfer = api.tx.balances.transfer(charlie.address, 1e15);
361 await submitTransactionAsync(alice, transfer);201 await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
362 });
363
364 await confirmSponsorshipExpectFailure(collectionId, '//Charlie');
365 });202 });
366203
367 it('(!negative test!) Confirm sponsorship using owner address', async () => {204 itSub('(!negative test!) Confirm sponsorship using owner address', async ({helper}) => {
368 const collectionId = await createCollectionExpectSuccess();205 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
369 await setCollectionSponsorExpectSuccess(collectionId, bob.address);206 await collection.setSponsor(alice, bob.address);
370 await confirmSponsorshipExpectFailure(collectionId, '//Alice');207 const confirmSponsorshipTx = async () => collection.confirmSponsorship(alice);
208 await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
371 });209 });
372210
373 it('(!negative test!) Confirm sponsorship by collection admin', async () => {211 itSub('(!negative test!) Confirm sponsorship by collection admin', async ({helper}) => {
374 const collectionId = await createCollectionExpectSuccess();212 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
375 await setCollectionSponsorExpectSuccess(collectionId, bob.address);213 await collection.setSponsor(alice, bob.address);
376 await addCollectionAdminExpectSuccess(alice, collectionId, charlie.address);214 await collection.addAdmin(alice, {Substrate: charlie.address});
377 await confirmSponsorshipExpectFailure(collectionId, '//Charlie');215 const confirmSponsorshipTx = async () => collection.confirmSponsorship(charlie);
216 await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
378 });217 });
379218
380 it('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async () => {219 itSub('(!negative test!) Confirm sponsorship without sponsor being set with setCollectionSponsor', async ({helper}) => {
381 const collectionId = await createCollectionExpectSuccess();220 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
382 await confirmSponsorshipExpectFailure(collectionId, '//Bob');221 const confirmSponsorshipTx = async () => collection.confirmSponsorship(charlie);
222 await expect(confirmSponsorshipTx()).to.be.rejectedWith(/unique\.ConfirmUnsetSponsorFail/);
383 });223 });
384224
385 it('(!negative test!) Confirm sponsorship in a collection that was destroyed', async () => {225 itSub('(!negative test!) Confirm sponsorship in a collection that was destroyed', async ({helper}) => {
386 const collectionId = await createCollectionExpectSuccess();226 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
387 await destroyCollectionExpectSuccess(collectionId);227 await collection.burn(alice);
388 await confirmSponsorshipExpectFailure(collectionId, '//Bob');228 const confirmSponsorshipTx = async () => collection.confirmSponsorship(charlie);
229 await expect(confirmSponsorshipTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
389 });230 });
390231
391 it('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async () => {232 itSub('(!negative test!) Transfer fees are not paid by the sponsor if the transfer failed', async ({helper}) => {
392 const collectionId = await createCollectionExpectSuccess();233 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
393 await setCollectionSponsorExpectSuccess(collectionId, bob.address);
394 await confirmSponsorshipExpectSuccess(collectionId, '//Bob');
395
396 await usingApi(async (api, privateKeyWrapper) => {234 await collection.setSponsor(alice, bob.address);
397 // Find unused address
398 const ownerZeroBalance = await findUnusedAddress(api, privateKeyWrapper);
399
400 // Find another unused address235 await collection.confirmSponsorship(bob);
401 const senderZeroBalance = await findUnusedAddress(api, privateKeyWrapper);
402
403 // Mint token for an unused address236 const token = await collection.mintToken(alice, {Substrate: ownerZeroBalance.address});
404 const itemId = await createItemExpectSuccess(alice, collectionId, 'NFT', ownerZeroBalance.address);
405
406 const sponsorBalanceBeforeTx = (await api.query.system.account(bob.address)).data.free.toBigInt();237 const sponsorBalanceBefore = await helper.balance.getSubstrate(bob.address);
407
408 // Try to transfer this token from an unsponsored unused adress to Alice238 const transferTx = async () => token.transfer(senderZeroBalance, {Substrate: alice.address});
409 const zeroToAlice = api.tx.unique.transfer(normalizeAccountId(alice.address), collectionId, itemId, 0);
410 await expect(submitTransactionExpectFailAsync(senderZeroBalance, zeroToAlice)).to.be.rejected;239 await expect(transferTx()).to.be.rejectedWith('Inability to pay some fees');
411
412 const sponsorBalanceAfterTx = (await api.query.system.account(bob.address)).data.free.toBigInt();240 const sponsorBalanceAfter = await helper.balance.getSubstrate(bob.address);
413
414 expect(sponsorBalanceAfterTx).to.equal(sponsorBalanceBeforeTx);241 expect(sponsorBalanceAfter).to.equal(sponsorBalanceBefore);
415 });
416 });242 });
417});243});
418244
modifiedtests/src/createCollection.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {IKeyringPair} from '@polkadot/types/types';
17import {expect} from 'chai';18import {usingPlaygrounds, expect, itSub, Pallets} from './util/playgrounds';
18import usingApi, {executeTransaction, submitTransactionAsync} from './substrate/substrate-api';19import {ICollectionCreationOptions, IProperty} from './util/playgrounds/types';
19import {createCollectionWithPropsExpectFailure, createCollectionExpectFailure, createCollectionExpectSuccess, getCreateCollectionResult, getDetailedCollectionInfo, createCollectionWithPropsExpectSuccess, requirePallets, Pallets} from './util/helpers';20import {UniqueHelper} from './util/playgrounds/unique';
21
22async function mintCollectionHelper(helper: UniqueHelper, signer: IKeyringPair, options: ICollectionCreationOptions, type?: 'nft' | 'fungible' | 'refungible') {
23 let collection;
24 if (type === 'nft') {
25 collection = await helper.nft.mintCollection(signer, options);
26 } else if (type === 'fungible') {
27 collection = await helper.ft.mintCollection(signer, options, 0);
28 } else {
29 collection = await helper.rft.mintCollection(signer, options);
30 }
31 const data = await collection.getData();
32 expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(signer.address));
33 expect(data?.name).to.be.equal(options.name);
34 expect(data?.description).to.be.equal(options.description);
35 expect(data?.raw.tokenPrefix).to.be.equal(options.tokenPrefix);
36 if (options.properties) {
37 expect(data?.raw.properties).to.be.deep.equal(options.properties);
38 }
39
40 if (options.tokenPropertyPermissions) {
41 expect(data?.raw.tokenPropertyPermissions).to.be.deep.equal(options.tokenPropertyPermissions);
42 }
43
44 return collection;
45}
2046
21describe('integration test: ext. createCollection():', () => {47describe('integration test: ext. createCollection():', () => {
48 let alice: IKeyringPair;
49
50 before(async () => {
51 await usingPlaygrounds(async (helper, privateKey) => {
52 const donor = privateKey('//Alice');
53 [alice] = await helper.arrange.createAccounts([100n], donor);
54 });
55 });
22 it('Create new NFT collection', async () => {56 itSub('Create new NFT collection', async ({helper}) => {
23 await createCollectionExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}});57 await mintCollectionHelper(helper, alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 'nft');
24 });58 });
25 it('Create new NFT collection whith collection_name of maximum length (64 bytes)', async () => {59 itSub('Create new NFT collection whith collection_name of maximum length (64 bytes)', async ({helper}) => {
26 await createCollectionExpectSuccess({name: 'A'.repeat(64)});60 await mintCollectionHelper(helper, alice, {name: 'A'.repeat(64), description: 'descr', tokenPrefix: 'COL'}, 'nft');
27 });61 });
28 it('Create new NFT collection whith collection_description of maximum length (256 bytes)', async () => {62 itSub('Create new NFT collection whith collection_description of maximum length (256 bytes)', async ({helper}) => {
29 await createCollectionExpectSuccess({description: 'A'.repeat(256)});63 await mintCollectionHelper(helper, alice, {name: 'name', description: 'A'.repeat(256), tokenPrefix: 'COL'}, 'nft');
30 });64 });
31 it('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async () => {65 itSub('Create new NFT collection whith token_prefix of maximum length (16 bytes)', async ({helper}) => {
32 await createCollectionExpectSuccess({tokenPrefix: 'A'.repeat(16)});66 await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(16)}, 'nft');
33 });67 });
34 it('Create new Fungible collection', async () => {
35 await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
36 });
37 it('Create new ReFungible collection', async function() {
38 await requirePallets(this, [Pallets.ReFungible]);
39
40 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
41 });
4268
43 it('create new collection with properties #1', async () => {69 itSub('Create new Fungible collection', async ({helper}) => {
44 await createCollectionWithPropsExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'},70 await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible');
45 properties: [{key: 'key1', value: 'val1'}],
46 propPerm: [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}]});
47 });71 });
4872
49 it('create new collection with properties #2', async () => {73 itSub.ifWithPallets('Create new ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
50 await createCollectionWithPropsExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'},74 await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'refungible');
51 properties: [{key: 'key1', value: 'val1'}],
52 propPerm: [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}]});
53 });75 });
5476
55 it('create new collection with properties #3', async () => {77 itSub('create new collection with properties', async ({helper}) => {
56 await createCollectionWithPropsExpectSuccess({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'},78 await mintCollectionHelper(helper, alice, {
79 name: 'name', description: 'descr', tokenPrefix: 'COL',
57 properties: [{key: 'key1', value: 'val1'}],80 properties: [{key: 'key1', value: 'val1'}],
58 propPerm: [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}]});81 tokenPropertyPermissions: [{key: 'key1', permission: {tokenOwner: true, mutable: false, collectionAdmin: true}}],
82 }, 'nft');
59 });83 });
6084
61 it('Create new collection with extra fields', async () => {85 itSub('Create new collection with extra fields', async ({helper}) => {
62 await usingApi(async (api, privateKeyWrapper) => {86 const collection = await mintCollectionHelper(helper, alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'}, 'fungible');
63 const alice = privateKeyWrapper('//Alice');87 await collection.setPermissions(alice, {access: 'AllowList'});
64 const bob = privateKeyWrapper('//Bob');88 await collection.setLimits(alice, {accountTokenOwnershipLimit: 3});
65 const tx = api.tx.unique.createCollectionEx({89 const data = await collection.getData();
66 mode: {Fungible: 8},
67 permissions: {
68 access: 'AllowList',
69 },
70 name: [1],
71 description: [2],
72 tokenPrefix: '0x000000',
73 pendingSponsor: bob.address,
74 limits: {
75 accountTokenOwnershipLimit: 3,
76 },
77 });
78 const events = await submitTransactionAsync(alice, tx);90 const limits = await collection.getEffectiveLimits();
79 const result = getCreateCollectionResult(events);91 const raw = data?.raw;
8092
81 const collection = (await getDetailedCollectionInfo(api, result.collectionId))!;
82 expect(collection.owner.toString()).to.equal(alice.address);93 expect(data?.normalizedOwner).to.be.equal(helper.address.normalizeSubstrate(alice.address));
83 expect(collection.mode.asFungible.toNumber()).to.equal(8);
84 expect(collection.permissions.access.toHuman()).to.equal('AllowList');
85 expect(collection.name.map(v => v.toNumber())).to.deep.equal([1]);94 expect(data?.name).to.be.equal('name');
86 expect(collection.description.map(v => v.toNumber())).to.deep.equal([2]);95 expect(data?.description).to.be.equal('descr');
87 expect(collection.tokenPrefix.toString()).to.equal('0x000000');96 expect(raw.permissions.access).to.be.equal('AllowList');
88 expect(collection.sponsorship.asUnconfirmed.toString()).to.equal(bob.address);97 expect(raw.mode).to.be.deep.equal({Fungible: '0'});
89 expect(collection.limits.accountTokenOwnershipLimit.unwrap().toNumber()).to.equal(3);98 expect(limits.accountTokenOwnershipLimit).to.be.equal(3);
90 });
91 });99 });
92100
93 it('New collection is not external', async () => {101 itSub('New collection is not external', async ({helper}) => {
94 await usingApi(async (api, privateKeyWrapper) => {
95 const alice = privateKeyWrapper('//Alice');102 const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL'});
96 const tx = api.tx.unique.createCollectionEx({ });
97 const events = await submitTransactionAsync(alice, tx);
98 const result = getCreateCollectionResult(events);
99
100 const collection = (await getDetailedCollectionInfo(api, result.collectionId))!;
101 expect(collection.readOnly.toHuman()).to.be.false;
102 });103 const data = await collection.getData();
104 expect(data?.raw.readOnly).to.be.false;
103 });105 });
104});106});
105107
106describe('(!negative test!) integration test: ext. createCollection():', () => {108describe('(!negative test!) integration test: ext. createCollection():', () => {
109 let alice: IKeyringPair;
110
111 before(async () => {
112 await usingPlaygrounds(async (helper, privateKey) => {
113 const donor = privateKey('//Alice');
114 [alice] = await helper.arrange.createAccounts([100n], donor);
115 });
116 });
117
107 it('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async () => {118 itSub('(!negative test!) create new NFT collection whith incorrect data (collection_name)', async ({helper}) => {
108 await createCollectionExpectFailure({name: 'A'.repeat(65), mode: {type: 'NFT'}});119 const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'A'.repeat(65), description: 'descr', tokenPrefix: 'COL'});
120 await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
109 });121 });
110 it('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async () => {122 itSub('(!negative test!) create new NFT collection whith incorrect data (collection_description)', async ({helper}) => {
111 await createCollectionExpectFailure({description: 'A'.repeat(257), mode: {type: 'NFT'}});123 const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'A'.repeat(257), tokenPrefix: 'COL'});
124 await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
112 });125 });
113 it('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async () => {126 itSub('(!negative test!) create new NFT collection whith incorrect data (token_prefix)', async ({helper}) => {
114 await createCollectionExpectFailure({tokenPrefix: 'A'.repeat(17), mode: {type: 'NFT'}});127 const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'A'.repeat(17)});
128 await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
115 });129 });
130
116 it('fails when bad limits are set', async () => {131 itSub('(!negative test!) fails when bad limits are set', async ({helper}) => {
117 await usingApi(async (api, privateKeyWrapper) => {132 const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', limits: {tokenLimit: 0}});
118 const alice = privateKeyWrapper('//Alice');133 await expect(mintCollectionTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
119 const tx = api.tx.unique.createCollectionEx({mode: 'NFT', limits: {tokenLimit: 0}});
120 await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/^common.CollectionTokenLimitExceeded$/);
121 });
122 });134 });
123135
124 it('(!negative test!) create collection with incorrect property limit (64 elements)', async () => {136 itSub('(!negative test!) create collection with incorrect property limit (64 elements)', async ({helper}) => {
125 const props = [];137 const props: IProperty[] = [];
126138
127 for (let i = 0; i < 65; i++) {139 for (let i = 0; i < 65; i++) {
128 props.push({key: `key${i}`, value: `value${i}`});140 props.push({key: `key${i}`, value: `value${i}`});
129 }141 }
130
131 await createCollectionWithPropsExpectFailure({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}, properties: props});142 const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
143 await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
132 });144 });
133145
134 it('(!negative test!) create collection with incorrect property limit (40 kb)', async () => {146 itSub('(!negative test!) create collection with incorrect property limit (40 kb)', async ({helper}) => {
135 const props = [];147 const props: IProperty[] = [];
136148
137 for (let i = 0; i < 32; i++) {149 for (let i = 0; i < 32; i++) {
138 props.push({key: `key${i}`.repeat(80), value: `value${i}`.repeat(80)});150 props.push({key: `key${i}`.repeat(80), value: `value${i}`.repeat(80)});
139 }151 }
140152
141 await createCollectionWithPropsExpectFailure({name: 'A', description: 'B', tokenPrefix: 'C', mode: {type: 'NFT'}, properties: props});153 const mintCollectionTx = async () => helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL', properties: props});
154 await expect(mintCollectionTx()).to.be.rejectedWith('Verification Error');
142 });155 });
143});156});
144157
modifiedtests/src/createItem.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {default as usingApi, executeTransaction} from './substrate/substrate-api';
18import chai from 'chai';
19import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
20import {18import {usingPlaygrounds, expect, itSub, Pallets} from './util/playgrounds';
21 createCollectionExpectSuccess,
22 createItemExpectSuccess,
23 addCollectionAdminExpectSuccess,
24 createCollectionWithPropsExpectSuccess,
25 createItemWithPropsExpectSuccess,
26 createItemWithPropsExpectFailure,19import {IProperty, ICrossAccountId} from './util/playgrounds/types';
27 createCollection,
28 transferExpectSuccess,
29 itApi,20import {UniqueHelper} from './util/playgrounds/unique';
30 normalizeAccountId,
31 getCreateItemResult,
32 requirePallets,
33 Pallets,
34} from './util/helpers';
3521
22async function mintTokenHelper(helper: UniqueHelper, collection: any, signer: IKeyringPair, owner: ICrossAccountId, type: 'nft' | 'fungible' | 'refungible'='nft', properties?: IProperty[]) {
23 let token;
36const expect = chai.expect;24 const itemCountBefore = await helper.collection.getLastTokenId(collection.collectionId);
37let alice: IKeyringPair;25 const itemBalanceBefore = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt();
38let bob: IKeyringPair;26 if (type === 'nft') {
27 token = await collection.mintToken(signer, owner, properties);
28 } else if (type === 'fungible') {
29 await collection.mint(signer, 10n, owner);
30 } else {
31 token = await collection.mintToken(signer, 100n, owner, properties);
32 }
3933
34 const itemCountAfter = await helper.collection.getLastTokenId(collection.collectionId);
35 const itemBalanceAfter = (await helper.callRpc('api.rpc.unique.balance', [collection.collectionId, owner, 0])).toBigInt();
36
37 if (type === 'fungible') {
38 expect(itemBalanceAfter - itemBalanceBefore).to.be.equal(10n);
39 } else {
40 expect(itemCountAfter).to.be.equal(itemCountBefore + 1);
41 }
42
43 return token;
44}
45
46
40describe('integration test: ext. ():', () => {47describe('integration test: ext. ():', () => {
48 let alice: IKeyringPair;
49 let bob: IKeyringPair;
50
41 before(async () => {51 before(async () => {
42 await usingApi(async (api, privateKeyWrapper) => {52 await usingPlaygrounds(async (helper, privateKey) => {
43 alice = privateKeyWrapper('//Alice');53 const donor = privateKey('//Alice');
44 bob = privateKeyWrapper('//Bob');54 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
45 });55 });
46 });56 });
4757
48 it('Create new item in NFT collection', async () => {58 itSub('Create new item in NFT collection', async ({helper}) => {
49 const createMode = 'NFT';59 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
50 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
51 await createItemExpectSuccess(alice, newCollectionID, createMode);60 await mintTokenHelper(helper, collection, alice, {Substrate: alice.address});
52 });61 });
53 it('Create new item in Fungible collection', async () => {62 itSub('Create new item in Fungible collection', async ({helper}) => {
54 const createMode = 'Fungible';63 const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
55 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
56 await createItemExpectSuccess(alice, newCollectionID, createMode);64 await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'fungible');
57 });65 });
58 itApi('Check events on create new item in Fungible collection', async ({api}) => {66 itSub('Check events on create new item in Fungible collection', async ({helper}) => {
59 const createMode = 'Fungible';67 const {collectionId} = await helper.ft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'}, 0);
60
61 const newCollectionID = (await createCollection(api, alice, {mode: {type: createMode, decimalPoints: 0}})).collectionId;
62 68 const to = {Substrate: alice.address};
63 const to = normalizeAccountId(alice);
64 {69 {
65 const createData = {fungible: {value: 100}};70 const createData = {fungible: {value: 100}};
66 const tx = api.tx.unique.createItem(newCollectionID, to, createData as any);71 const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]);
67 const events = await executeTransaction(api, alice, tx);72 const result = helper.util.extractTokensFromCreationResult(events);
68 const result = getCreateItemResult(events);
69 expect(result.amount).to.be.equal(100);73 expect(result.tokens[0].amount).to.be.equal(100n);
70 expect(result.collectionId).to.be.equal(newCollectionID);74 expect(result.tokens[0].collectionId).to.be.equal(collectionId);
71 expect(result.recipient).to.be.deep.equal(to);75 expect(result.tokens[0].owner).to.be.deep.equal(to);
72 }76 }
73 {77 {
74 const createData = {fungible: {value: 50}};78 const createData = {fungible: {value: 50}};
75 const tx = api.tx.unique.createItem(newCollectionID, to, createData as any);79 const events = await helper.executeExtrinsic(alice, 'api.tx.unique.createItem', [collectionId, to, createData as any]);
76 const events = await executeTransaction(api, alice, tx);80 const result = helper.util.extractTokensFromCreationResult(events);
77 const result = getCreateItemResult(events);
78 expect(result.amount).to.be.equal(50);81 expect(result.tokens[0].amount).to.be.equal(50n);
79 expect(result.collectionId).to.be.equal(newCollectionID);82 expect(result.tokens[0].collectionId).to.be.equal(collectionId);
80 expect(result.recipient).to.be.deep.equal(to);83 expect(result.tokens[0].owner).to.be.deep.equal(to);
81 }84 }
82
83 });85 });
84 it('Create new item in ReFungible collection', async function() {86 itSub.ifWithPallets('Create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
85 await requirePallets(this, [Pallets.ReFungible]);
86
87 const createMode = 'ReFungible';
88 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});87 const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
89 await createItemExpectSuccess(alice, newCollectionID, createMode);88 await mintTokenHelper(helper, collection, alice, {Substrate: alice.address}, 'refungible');
90 });89 });
91 it('Create new item in NFT collection with collection admin permissions', async () => {90 itSub('Create new item in NFT collection with collection admin permissions', async ({helper}) => {
92 const createMode = 'NFT';91 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
93 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});
94 await addCollectionAdminExpectSuccess(alice, newCollectionID, bob.address);92 await collection.addAdmin(alice, {Substrate: bob.address});
95 await createItemExpectSuccess(bob, newCollectionID, createMode);93 await mintTokenHelper(helper, collection, bob, {Substrate: alice.address});
96 });94 });
97 it('Create new item in Fungible collection with collection admin permissions', async () => {95 itSub('Create new item in Fungible collection with collection admin permissions', async ({helper}) => {
98 const createMode = 'Fungible';96 const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
99 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
100 await addCollectionAdminExpectSuccess(alice, newCollectionID, bob.address);97 await collection.addAdmin(alice, {Substrate: bob.address});
101 await createItemExpectSuccess(bob, newCollectionID, createMode);98 await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'fungible');
102 });99 });
103 it('Create new item in ReFungible collection with collection admin permissions', async function() {100 itSub.ifWithPallets('Create new item in ReFungible collection with collection admin permissions', [Pallets.ReFungible], async ({helper}) => {
104 await requirePallets(this, [Pallets.ReFungible]);
105
106 const createMode = 'ReFungible';
107 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});101 const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
108 await addCollectionAdminExpectSuccess(alice, newCollectionID, bob.address);102 await collection.addAdmin(alice, {Substrate: bob.address});
109 await createItemExpectSuccess(bob, newCollectionID, createMode);103 await mintTokenHelper(helper, collection, bob, {Substrate: alice.address}, 'refungible');
110 });104 });
111105
112 it('Set property Admin', async () => {106 itSub('Set property Admin', async ({helper}) => {
113 const createMode = 'NFT';107 const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
114 const newCollectionID = await createCollectionWithPropsExpectSuccess({mode: {type: createMode}, 108 properties: [{key: 'k', value: 'v'}],
115 propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: false}}]});109 tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}}],
116 110 });
117 await createItemWithPropsExpectSuccess(alice, newCollectionID, createMode, [{key: 'k', value: 't2'}]);111 await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
118 });112 });
119113
120 it('Set property AdminConst', async () => {114 itSub('Set property AdminConst', async ({helper}) => {
121 const createMode = 'NFT';115 const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
122 const newCollectionID = await createCollectionWithPropsExpectSuccess({mode: {type: createMode}, 116 properties: [{key: 'k', value: 'v'}],
123 propPerm: [{key: 'key1', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}]});117 tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}}],
124 118 });
125 await createItemWithPropsExpectSuccess(alice, newCollectionID, createMode, [{key: 'key1', value: 'val1'}]);119 await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
126 });120 });
127121
128 it('Set property itemOwnerOrAdmin', async () => {122 itSub('Set property itemOwnerOrAdmin', async ({helper}) => {
129 const createMode = 'NFT';123 const collection = await helper.nft.mintCollection(alice, {name: 'name', description: 'descr', tokenPrefix: 'COL',
130 const newCollectionID = await createCollectionWithPropsExpectSuccess({mode: {type: createMode},124 properties: [{key: 'k', value: 'v'}],
131 propPerm: [{key: 'key1', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]});125 tokenPropertyPermissions: [{key: 'k', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}],
132 126 });
133 await createItemWithPropsExpectSuccess(alice, newCollectionID, createMode, [{key: 'key1', value: 'val1'}]);127 await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'nft', [{key: 'k', value: 'v'}]);
134 });128 });
135129
136 it('Check total pieces of Fungible token', async () => {130 itSub('Check total pieces of Fungible token', async ({helper}) => {
137 await usingApi(async api => {131 const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
138 const createMode = 'Fungible';
139 const collectionId = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});
140 const amountPieces = 10n;132 const amount = 10n;
141 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode, bob.address);133 await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'fungible');
142 {134 {
143 const totalPieces = await api.rpc.unique.totalPieces(collectionId, tokenId);135 const totalPieces = await collection.getTotalPieces();
144 expect(totalPieces.isSome).to.be.true;136 expect(totalPieces).to.be.equal(amount);
145 expect(totalPieces.unwrap().toBigInt()).to.be.eq(amountPieces);
146 }137 }
147
148 await transferExpectSuccess(collectionId, tokenId, bob, alice, 1, createMode);138 await collection.transfer(bob, {Substrate: alice.address}, 1n);
149 {
150 const totalPieces = await api.rpc.unique.totalPieces(collectionId, tokenId);
151 expect(totalPieces.isSome).to.be.true;139 {
152 expect(totalPieces.unwrap().toBigInt()).to.be.eq(amountPieces);140 const totalPieces = await collection.getTotalPieces();
153 }
154
155 const totalPieces = (await api.rpc.unique.tokenData(collectionId, tokenId, [])).pieces;
156 expect(totalPieces.toBigInt()).to.be.eq(amountPieces);141 expect(totalPieces).to.be.equal(amount);
157 });142 }
158 });143 });
159144
160 it('Check total pieces of NFT token', async () => {145 itSub('Check total pieces of NFT token', async ({helper}) => {
161 await usingApi(async api => {146 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
162 const createMode = 'NFT';
163 const collectionId = await createCollectionExpectSuccess({mode: {type: createMode}});
164 const amountPieces = 1n;147 const amount = 1n;
165 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode, bob.address);148 const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address});
166 {149 {
167 const totalPieces = await api.rpc.unique.totalPieces(collectionId, tokenId);150 const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]);
168 expect(totalPieces.isSome).to.be.true;151 expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount);
169 expect(totalPieces.unwrap().toBigInt()).to.be.eq(amountPieces);
170 }152 }
171
172 await transferExpectSuccess(collectionId, tokenId, bob, alice, 1, createMode);153 await token.transfer(bob, {Substrate: alice.address});
173 {154 {
174 const totalPieces = await api.rpc.unique.totalPieces(collectionId, tokenId);155 const totalPieces = await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, token.tokenId]);
175 expect(totalPieces.isSome).to.be.true;156 expect(totalPieces?.unwrap().toBigInt()).to.be.equal(amount);
176 expect(totalPieces.unwrap().toBigInt()).to.be.eq(amountPieces);
177 }157 }
178
179 const totalPieces = (await api.rpc.unique.tokenData(collectionId, tokenId, [])).pieces;
180 expect(totalPieces.toBigInt()).to.be.eq(amountPieces);
181 });
182 });158 });
183159
184 it('Check total pieces of ReFungible token', async function() {160 itSub.ifWithPallets('Check total pieces of ReFungible token', [Pallets.ReFungible], async ({helper}) => {
185 await requirePallets(this, [Pallets.ReFungible]);
186
187 await usingApi(async api => {
188 const createMode = 'ReFungible';161 const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
189 const createCollectionResult = await createCollection(api, alice, {mode: {type: createMode}});
190 const collectionId = createCollectionResult.collectionId;162 const amount = 100n;
191 const amountPieces = 100n;
192 const tokenId = await createItemExpectSuccess(alice, collectionId, createMode, bob.address);163 const token = await mintTokenHelper(helper, collection, alice, {Substrate: bob.address}, 'refungible');
193 {164 {
194 const totalPieces = await api.rpc.unique.totalPieces(collectionId, tokenId);165 const totalPieces = await token.getTotalPieces();
195 expect(totalPieces.isSome).to.be.true;166 expect(totalPieces).to.be.equal(amount);
196 expect(totalPieces.unwrap().toBigInt()).to.be.eq(amountPieces);
197 }167 }
198
199 await transferExpectSuccess(collectionId, tokenId, bob, alice, 60n, createMode);168 await token.transfer(bob, {Substrate: alice.address}, 60n);
200 {169 {
201 const totalPieces = await api.rpc.unique.totalPieces(collectionId, tokenId);170 const totalPieces = await token.getTotalPieces();
202 expect(totalPieces.isSome).to.be.true;171 expect(totalPieces).to.be.equal(amount);
203 expect(totalPieces.unwrap().toBigInt()).to.be.eq(amountPieces);
204 }172 }
205
206 const totalPieces = (await api.rpc.unique.tokenData(collectionId, tokenId, [])).pieces;
207 expect(totalPieces.toBigInt()).to.be.eq(amountPieces);
208 });
209 });173 });
210});174});
211175
212describe('Negative integration test: ext. createItem():', () => {176describe('Negative integration test: ext. createItem():', () => {
177 let alice: IKeyringPair;
178 let bob: IKeyringPair;
179
213 before(async () => {180 before(async () => {
214 await usingApi(async (api, privateKeyWrapper) => {181 await usingPlaygrounds(async (helper, privateKey) => {
215 alice = privateKeyWrapper('//Alice');182 const donor = privateKey('//Alice');
216 bob = privateKeyWrapper('//Bob');183 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
217 });184 });
218 });185 });
219186
220 it('Regular user cannot create new item in NFT collection', async () => {187 itSub('Regular user cannot create new item in NFT collection', async ({helper}) => {
221 const createMode = 'NFT';188 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
222 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});189 const mintTx = async () => collection.mintToken(bob, {Substrate: bob.address});
223 await expect(createItemExpectSuccess(bob, newCollectionID, createMode)).to.be.rejected;190 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
224 });191 });
225 it('Regular user cannot create new item in Fungible collection', async () => {192 itSub('Regular user cannot create new item in Fungible collection', async ({helper}) => {
226 const createMode = 'Fungible';193 const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
227 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode, decimalPoints: 0}});194 const mintTx = async () => collection.mint(bob, 10n, {Substrate: bob.address});
228 await expect(createItemExpectSuccess(bob, newCollectionID, createMode)).to.be.rejected;195 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
229 });196 });
230 it('Regular user cannot create new item in ReFungible collection', async function() {197 itSub.ifWithPallets('Regular user cannot create new item in ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
231 await requirePallets(this, [Pallets.ReFungible]);198 const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
232
233 const createMode = 'ReFungible';
234 const newCollectionID = await createCollectionExpectSuccess({mode: {type: createMode}});199 const mintTx = async () => collection.mintToken(bob, 100n, {Substrate: bob.address});
235 await expect(createItemExpectSuccess(bob, newCollectionID, createMode)).to.be.rejected;200 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
236 });201 });
237202
238 it('No editing rights', async () => {203 itSub('No editing rights', async ({helper}) => {
239 await usingApi(async () => {
240 const createMode = 'NFT';204 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
241 const newCollectionID = await createCollectionWithPropsExpectSuccess({mode: {type: createMode},
242 propPerm: [{key: 'key1', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}]});205 tokenPropertyPermissions: [{key: 'k', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}],
243 await addCollectionAdminExpectSuccess(alice, newCollectionID, bob.address);
244
245 await createItemWithPropsExpectFailure(bob, newCollectionID, 'NFT', [{key: 'key1', value: 'v'}]);
246 });206 });
207 const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
208 await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
247 });209 });
248210
249 it('User doesnt have editing rights', async () => {211 itSub('User doesnt have editing rights', async ({helper}) => {
250 await usingApi(async () => {212 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
251 const newCollectionID = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'key1', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}]});213 tokenPropertyPermissions: [{key: 'k', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}],
252 await createItemWithPropsExpectFailure(bob, newCollectionID, 'NFT', [{key: 'key1', value: 'v'}]);
253 });214 });
215 const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
216 await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
254 });217 });
255218
256 it('Adding property without access rights', async () => {219 itSub('Adding property without access rights', async ({helper}) => {
257 await usingApi(async () => {220 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
258 const newCollectionID = await createCollectionWithPropsExpectSuccess();221 const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, [{key: 'k', value: 'v'}]);
259 await addCollectionAdminExpectSuccess(alice, newCollectionID, bob.address);
260
261 await createItemWithPropsExpectFailure(bob, newCollectionID, 'NFT', [{key: 'k', value: 'v'}]);
262 });222 await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
263 });223 });
264224
265 it('Adding more than 64 prps', async () => {225 itSub('Adding more than 64 prps', async ({helper}) => {
266 await usingApi(async () => {
267 const prps = [];226 const props: IProperty[] = [];
268227
269 for (let i = 0; i < 65; i++) {228 for (let i = 0; i < 65; i++) {
270 prps.push({key: `key${i}`, value: `value${i}`});229 props.push({key: `key${i}`, value: `value${i}`});
271 }230 }
272231
232
273 const newCollectionID = await createCollectionWithPropsExpectSuccess();233 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
274
275 await createItemWithPropsExpectFailure(alice, newCollectionID, 'NFT', prps);234 const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, props);
276 });235 await expect(mintTx()).to.be.rejectedWith('Verification Error');
277 });236 });
278237
279 it('Trying to add bigger property than allowed', async () => {238 itSub('Trying to add bigger property than allowed', async ({helper}) => {
280 await usingApi(async () => {239 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL',
281 const newCollectionID = await createCollectionWithPropsExpectSuccess();240 tokenPropertyPermissions: [
282
283 await createItemWithPropsExpectFailure(alice, newCollectionID, 'NFT', [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]);241 {key: 'k1', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
242 {key: 'k2', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}},
243 ],
284 });244 });
245 const mintTx = async () => collection.mintToken(alice, {Substrate: bob.address}, [
246 {key: 'k1', value: 'vvvvvv'.repeat(5000)},
247 {key: 'k2', value: 'vvv'.repeat(5000)},
248 ]);
249 await expect(mintTx()).to.be.rejectedWith(/common\.NoSpaceForProperty/);
285 });250 });
286251
287 it('Check total pieces for invalid Fungible token', async () => {252 itSub('Check total pieces for invalid Fungible token', async ({helper}) => {
288 await usingApi(async api => {253 const collection = await helper.ft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'}, 0);
289 const createCollectionResult = await createCollection(api, alice, {mode: {type: 'Fungible', decimalPoints: 0}});
290 const collectionId = createCollectionResult.collectionId;254 const invalidTokenId = 1_000_000;
291 const invalidTokenId = 1000_000;
292 255 expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
293 expect((await api.rpc.unique.totalPieces(collectionId, invalidTokenId)).isNone).to.be.true;
294 expect((await api.rpc.unique.tokenData(collectionId, invalidTokenId, [])).pieces.toBigInt()).to.be.eq(0n);256 expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
295 });
296 });257 });
297258
298 it('Check total pieces for invalid NFT token', async () => {259 itSub('Check total pieces for invalid NFT token', async ({helper}) => {
299 await usingApi(async api => {260 const collection = await helper.nft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
300 const createCollectionResult = await createCollection(api, alice, {mode: {type: 'NFT'}});
301 const collectionId = createCollectionResult.collectionId;261 const invalidTokenId = 1_000_000;
302 const invalidTokenId = 1000_000;
303 262 expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
304 expect((await api.rpc.unique.totalPieces(collectionId, invalidTokenId)).isNone).to.be.true;
305 expect((await api.rpc.unique.tokenData(collectionId, invalidTokenId, [])).pieces.toBigInt()).to.be.eq(0n);263 expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
306 });
307 });264 });
308265
309 it('Check total pieces for invalid Refungible token', async function() {266 itSub.ifWithPallets('Check total pieces for invalid Refungible token', [Pallets.ReFungible], async ({helper}) => {
310 await requirePallets(this, [Pallets.ReFungible]);
311
312 await usingApi(async api => {
313 const createCollectionResult = await createCollection(api, alice, {mode: {type: 'ReFungible'}});267 const collection = await helper.rft.mintCollection(alice, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
314 const collectionId = createCollectionResult.collectionId;268 const invalidTokenId = 1_000_000;
315 const invalidTokenId = 1000_000;
316 269 expect((await helper.callRpc('api.rpc.unique.totalPieces', [collection.collectionId, invalidTokenId]))?.isNone).to.be.true;
317 expect((await api.rpc.unique.totalPieces(collectionId, invalidTokenId)).isNone).to.be.true;
318 expect((await api.rpc.unique.tokenData(collectionId, invalidTokenId, [])).pieces.toBigInt()).to.be.eq(0n);270 expect((await helper.callRpc('api.rpc.unique.tokenData', [collection.collectionId, invalidTokenId]))?.pieces.toBigInt()).to.be.equal(0n);
319 });
320 });271 });
321});272});
322273
modifiedtests/src/createMultipleItems.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {ApiPromise} from '@polkadot/api';
18import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
19import chai from 'chai';18import {usingPlaygrounds, expect, Pallets, itSub} from './util/playgrounds';
20import chaiAsPromised from 'chai-as-promised';
21import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync, executeTransaction} from './substrate/substrate-api';
22import {
23 createCollectionExpectSuccess,
24 destroyCollectionExpectSuccess,
25 getGenericResult,
26 normalizeAccountId,
27 setCollectionLimitsExpectSuccess,
28 addCollectionAdminExpectSuccess,
29 getBalance,
30 getTokenOwner,
31 getLastTokenId,
32 getCreatedCollectionCount,
33 createCollectionWithPropsExpectSuccess,
34 createMultipleItemsWithPropsExpectSuccess,
35 getTokenProperties,
36 requirePallets,
37 Pallets,
38 checkPalletsPresence,
39} from './util/helpers';
4019
41chai.use(chaiAsPromised);
42const expect = chai.expect;
43
44describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {20describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
45 it('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async () => {21 let alice: IKeyringPair;
46 await usingApi(async (api, privateKeyWrapper) => {
47 const collectionId = await createCollectionExpectSuccess();
48 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
49 expect(itemsListIndexBefore).to.be.equal(0);
5022
51 const alice = privateKeyWrapper('//Alice');23 before(async () => {
52 await submitTransactionAsync(24 await usingPlaygrounds(async (helper, privateKey) => {
53 alice,
54 api.tx.unique.setTokenPropertyPermissions(collectionId, [{key: 'data', permission: {tokenOwner: true}}]),
55 );
56
57 const args = [
58 {NFT: {properties: [{key: 'data', value: '1'}]}},
59 {NFT: {properties: [{key: 'data', value: '2'}]}},25 const donor = privateKey('//Alice');
60 {NFT: {properties: [{key: 'data', value: '3'}]}},
61 ];
62 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
63 await submitTransactionAsync(alice, createMultipleItemsTx);26 [alice] = await helper.arrange.createAccounts([100n], donor);
64 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
65 expect(itemsListIndexAfter).to.be.equal(3);
66
67 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(alice.address));
68 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(alice.address));
69 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(alice.address));
70
71 expect((await getTokenProperties(api, collectionId, 1, ['data']))[0].value).to.be.equal('1');
72 expect((await getTokenProperties(api, collectionId, 2, ['data']))[0].value).to.be.equal('2');
73 expect((await getTokenProperties(api, collectionId, 3, ['data']))[0].value).to.be.equal('3');
74 });27 });
75 });28 });
7629
77 it('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async () => {30 itSub('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async ({helper}) => {
78 await usingApi(async (api, privateKeyWrapper) => {31 const collection = await helper.nft.mintCollection(alice, {
79 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});32 name: 'name',
80 const itemsListIndexBefore = await getLastTokenId(api, collectionId);33 description: 'descr',
81 expect(itemsListIndexBefore).to.be.equal(0);34 tokenPrefix: 'COL',
82 const alice = privateKeyWrapper('//Alice');
83 const args = [35 tokenPropertyPermissions: [
84 {Fungible: {value: 1}},36 {key: 'data', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}},
85 {Fungible: {value: 2}},
86 {Fungible: {value: 3}},
87 ];37 ],
88 const createMultipleItemsTx = api.tx.unique
89 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
90 await submitTransactionAsync(alice, createMultipleItemsTx);
91 const token1Data = await getBalance(api, collectionId, alice.address, 0);
92
93 expect(token1Data).to.be.equal(6n); // 1 + 2 + 3
94 });38 });
39 const args = [
40 {properties: [{key: 'data', value: '1'}]},
41 {properties: [{key: 'data', value: '2'}]},
42 {properties: [{key: 'data', value: '3'}]},
43 ];
44 const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
45 for (const [i, token] of tokens.entries()) {
46 const tokenData = await token.getData();
47 expect(tokenData?.normalizedOwner.Substrate).to.be.deep.equal(helper.address.normalizeSubstrate(alice.address));
48 expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
49 }
95 });50 });
9651
97 it('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', async function() {52 itSub('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async ({helper}) => {
98 await requirePallets(this, [Pallets.ReFungible]);
99
100 await usingApi(async (api, privateKeyWrapper) => {
101 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});53 const collection = await helper.ft.mintCollection(alice, {
102 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
103 expect(itemsListIndexBefore).to.be.equal(0);
104 const alice = privateKeyWrapper('//Alice');
105 const args = [54 name: 'name',
106 {ReFungible: {pieces: 1}},
107 {ReFungible: {pieces: 2}},55 description: 'descr',
108 {ReFungible: {pieces: 3}},56 tokenPrefix: 'COL',
109 ];
110 const createMultipleItemsTx = api.tx.unique
111 .createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
112 await submitTransactionAsync(alice, createMultipleItemsTx);
113 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
114 expect(itemsListIndexAfter).to.be.equal(3);
115
116 expect(await getBalance(api, collectionId, alice.address, 1)).to.be.equal(1n);
117 expect(await getBalance(api, collectionId, alice.address, 2)).to.be.equal(2n);
118 expect(await getBalance(api, collectionId, alice.address, 3)).to.be.equal(3n);
119 });57 });
58 const args = [
59 {value: 1n},
60 {value: 2n},
61 {value: 3n},
62 ];
63 await helper.ft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, args, {Substrate: alice.address});
64 expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(6n);
120 });65 });
12166
122 it('Can mint amount of items equals to collection limits', async () => {67 itSub.ifWithPallets('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', [Pallets.ReFungible], async ({helper}) => {
123 await usingApi(async (api, privateKeyWrapper) => {
124 const alice = privateKeyWrapper('//Alice');68 const collection = await helper.rft.mintCollection(alice, {
125
126 const collectionId = await createCollectionExpectSuccess();
127 await setCollectionLimitsExpectSuccess(alice, collectionId, {
128 tokenLimit: 2,69 name: 'name',
129 });70 description: 'descr',
130 const args = [
131 {NFT: {}},
132 {NFT: {}},71 tokenPrefix: 'COL',
133 ];
134 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
135 const events = await submitTransactionAsync(alice, createMultipleItemsTx);
136 const result = getGenericResult(events);
137 expect(result.success).to.be.true;
138 });72 });
139 });73 const args = [
74 {pieces: 1n},
75 {pieces: 2n},
76 {pieces: 3n},
77 ];
78 const tokens = await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
14079
141 it('Create 0x31, 0x32, 0x33 items in active NFT with property Admin', async () => {80 for (const [i, token] of tokens.entries()) {
142 await usingApi(async (api, privateKeyWrapper) => {
143 const collectionId = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: false}}]});
144 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
145 expect(itemsListIndexBefore).to.be.equal(0);
146 const alice = privateKeyWrapper('//Alice');
147 const args = [
148 {NFT: {properties: [{key: 'k', value: 'v1'}]}},
149 {NFT: {properties: [{key: 'k', value: 'v2'}]}},81 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(BigInt(i + 1));
150 {NFT: {properties: [{key: 'k', value: 'v3'}]}},
151 ];
152
153 await createMultipleItemsWithPropsExpectSuccess(alice, collectionId, args);
154 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
155 expect(itemsListIndexAfter).to.be.equal(3);
156
157 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(alice.address));
158 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(alice.address));
159 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(alice.address));
160
161 expect((await getTokenProperties(api, collectionId, 1, ['k']))[0].value).to.be.equal('v1');
162 expect((await getTokenProperties(api, collectionId, 2, ['k']))[0].value).to.be.equal('v2');82 }
163 expect((await getTokenProperties(api, collectionId, 3, ['k']))[0].value).to.be.equal('v3');
164 });
165 });83 });
16684
167 it('Create 0x31, 0x32, 0x33 items in active NFT with property AdminConst', async () => {85 itSub('Can mint amount of items equals to collection limits', async ({helper}) => {
168 await usingApi(async (api, privateKeyWrapper) => {
169 const collectionId = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}]});86 const collection = await helper.nft.mintCollection(alice, {
170 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
171 expect(itemsListIndexBefore).to.be.equal(0);
172 const alice = privateKeyWrapper('//Alice');
173 const bob = privateKeyWrapper('//Bob');87 name: 'name',
174 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
175 const args = [88 description: 'descr',
176 {NFT: {properties: [{key: 'k', value: 'v1'}]}},
177 {NFT: {properties: [{key: 'k', value: 'v2'}]}},89 tokenPrefix: 'COL',
178 {NFT: {properties: [{key: 'k', value: 'v3'}]}},90 limits: {
179 ];91 tokenLimit: 2,
180
181 await createMultipleItemsWithPropsExpectSuccess(alice, collectionId, args);
182 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
183 expect(itemsListIndexAfter).to.be.equal(3);
184
185 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(alice.address));
186 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(alice.address));
187 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(alice.address));
188
189 expect((await getTokenProperties(api, collectionId, 1, ['k']))[0].value).to.be.equal('v1');92 },
190 expect((await getTokenProperties(api, collectionId, 2, ['k']))[0].value).to.be.equal('v2');
191 expect((await getTokenProperties(api, collectionId, 3, ['k']))[0].value).to.be.equal('v3');
192 });93 });
94 const args = [{}, {}];
95 await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
193 });96 });
19497
195 it('Create 0x31, 0x32, 0x33 items in active NFT with property itemOwnerOrAdmin', async () => {98 itSub('Create 0x31, 0x32, 0x33 items in active NFT with property Admin', async ({helper}) => {
196 await usingApi(async (api, privateKeyWrapper) => {
197 const collectionId = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]});99 const collection = await helper.nft.mintCollection(alice, {
198 const itemsListIndexBefore = await getLastTokenId(api, collectionId);100 name: 'name',
199 expect(itemsListIndexBefore).to.be.equal(0);101 description: 'descr',
200 const alice = privateKeyWrapper('//Alice');
201 const args = [102 tokenPrefix: 'COL',
202 {NFT: {properties: [{key: 'k', value: 'v1'}]}},103 tokenPropertyPermissions: [
203 {NFT: {properties: [{key: 'k', value: 'v2'}]}},104 {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}},
204 {NFT: {properties: [{key: 'k', value: 'v3'}]}},
205 ];105 ],
206
207 await createMultipleItemsWithPropsExpectSuccess(alice, collectionId, args);
208 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
209 expect(itemsListIndexAfter).to.be.equal(3);
210
211 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(alice.address));
212 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(alice.address));
213 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(alice.address));
214
215 expect((await getTokenProperties(api, collectionId, 1, ['k']))[0].value).to.be.equal('v1');
216 expect((await getTokenProperties(api, collectionId, 2, ['k']))[0].value).to.be.equal('v2');
217 expect((await getTokenProperties(api, collectionId, 3, ['k']))[0].value).to.be.equal('v3');
218 });106 });
107 const args = [
108 {properties: [{key: 'data', value: '1'}]},
109 {properties: [{key: 'data', value: '2'}]},
110 {properties: [{key: 'data', value: '3'}]},
111 ];
112 const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
113 for (const [i, token] of tokens.entries()) {
114 const tokenData = await token.getData();
115 expect(tokenData?.normalizedOwner.Substrate).to.be.deep.equal(helper.address.normalizeSubstrate(alice.address));
116 expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
117 }
219 });118 });
220});
221119
222describe('Integration Test createMultipleItems(collection_id, owner, items_data) with collection admin permissions:', () => {120 itSub('Create 0x31, 0x32, 0x33 items in active NFT with property AdminConst', async ({helper}) => {
223 let alice: IKeyringPair;121 const collection = await helper.nft.mintCollection(alice, {
224 let bob: IKeyringPair;122 name: 'name',
225
226 before(async () => {123 description: 'descr',
227 await usingApi(async (api, privateKeyWrapper) => {124 tokenPrefix: 'COL',
125 tokenPropertyPermissions: [
228 alice = privateKeyWrapper('//Alice');126 {key: 'data', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}},
229 bob = privateKeyWrapper('//Bob');127 ],
230 });128 });
129 const args = [
130 {properties: [{key: 'data', value: '1'}]},
131 {properties: [{key: 'data', value: '2'}]},
132 {properties: [{key: 'data', value: '3'}]},
133 ];
134 const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
135 for (const [i, token] of tokens.entries()) {
136 const tokenData = await token.getData();
137 expect(tokenData?.normalizedOwner.Substrate).to.be.deep.equal(helper.address.normalizeSubstrate(alice.address));
138 expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
139 }
231 });140 });
232141
233 it('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async () => {142 itSub('Create 0x31, 0x32, 0x33 items in active NFT with property itemOwnerOrAdmin', async ({helper}) => {
234 await usingApi(async (api: ApiPromise) => {
235 const collectionId = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'data', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]});143 const collection = await helper.nft.mintCollection(alice, {
236 const itemsListIndexBefore = await getLastTokenId(api, collectionId);144 name: 'name',
237 expect(itemsListIndexBefore).to.be.equal(0);145 description: 'descr',
238 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
239 const args = [146 tokenPrefix: 'COL',
240 {NFT: {properties: [{key: 'data', value: 'v1'}]}},147 tokenPropertyPermissions: [
241 {NFT: {properties: [{key: 'data', value: 'v2'}]}},148 {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
242 {NFT: {properties: [{key: 'data', value: 'v3'}]}},
243 ];149 ],
244 const createMultipleItemsTx = api.tx.unique
245 .createMultipleItems(collectionId, normalizeAccountId(bob.address), args);
246 await submitTransactionAsync(bob, createMultipleItemsTx);
247 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
248 expect(itemsListIndexAfter).to.be.equal(3);
249
250 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(bob.address));
251 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(bob.address));
252 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(bob.address));
253
254 expect((await getTokenProperties(api, collectionId, 1, ['data']))[0].value).to.be.equal('v1');
255 expect((await getTokenProperties(api, collectionId, 2, ['data']))[0].value).to.be.equal('v2');
256 expect((await getTokenProperties(api, collectionId, 3, ['data']))[0].value).to.be.equal('v3');
257 });150 });
151 const args = [
152 {properties: [{key: 'data', value: '1'}]},
153 {properties: [{key: 'data', value: '2'}]},
154 {properties: [{key: 'data', value: '3'}]},
155 ];
156 const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
157 for (const [i, token] of tokens.entries()) {
158 const tokenData = await token.getData();
159 expect(tokenData?.normalizedOwner.Substrate).to.be.equal(helper.address.normalizeSubstrate(alice.address));
160 expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);
161 }
258 });162 });
259
260 it('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async () => {
261 await usingApi(async (api: ApiPromise) => {
262 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});
263 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
264 expect(itemsListIndexBefore).to.be.equal(0);
265 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
266 const args = [
267 {Fungible: {value: 1}},
268 {Fungible: {value: 2}},
269 {Fungible: {value: 3}},
270 ];
271 const createMultipleItemsTx = api.tx.unique
272 .createMultipleItems(collectionId, normalizeAccountId(bob.address), args);
273 await submitTransactionAsync(bob, createMultipleItemsTx);
274 const token1Data = await getBalance(api, collectionId, bob.address, 0);
275
276 expect(token1Data).to.be.equal(6n); // 1 + 2 + 3
277 });
278 });
279
280 it('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', async function() {
281 await requirePallets(this, [Pallets.ReFungible]);
282
283 await usingApi(async (api: ApiPromise) => {
284 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
285 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
286 expect(itemsListIndexBefore).to.be.equal(0);
287 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
288 const args = [
289 {ReFungible: {pieces: 1}},
290 {ReFungible: {pieces: 2}},
291 {ReFungible: {pieces: 3}},
292 ];
293 const createMultipleItemsTx = api.tx.unique
294 .createMultipleItems(collectionId, normalizeAccountId(bob.address), args);
295 await submitTransactionAsync(bob, createMultipleItemsTx);
296 const itemsListIndexAfter = await getLastTokenId(api, collectionId);
297 expect(itemsListIndexAfter).to.be.equal(3);
298
299 expect(await getBalance(api, collectionId, bob.address, 1)).to.be.equal(1n);
300 expect(await getBalance(api, collectionId, bob.address, 2)).to.be.equal(2n);
301 expect(await getBalance(api, collectionId, bob.address, 3)).to.be.equal(3n);
302 });
303 });
304});163});
305164
306describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => {165describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => {
307 let alice: IKeyringPair;166 let alice: IKeyringPair;
308 let bob: IKeyringPair;167 let bob: IKeyringPair;
309168
310 before(async () => {169 before(async () => {
311 await usingApi(async (api, privateKeyWrapper) => {170 await usingPlaygrounds(async (helper, privateKey) => {
312 alice = privateKeyWrapper('//Alice');171 const donor = privateKey('//Alice');
313 bob = privateKeyWrapper('//Bob');172 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
314 });173 });
315 });174 });
316175
317 it('Regular user cannot create items in active NFT collection', async () => {176 itSub('Regular user cannot create items in active NFT collection', async ({helper}) => {
318 await usingApi(async (api: ApiPromise) => {
319 const collectionId = await createCollectionExpectSuccess();177 const collection = await helper.nft.mintCollection(alice, {
320 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
321 expect(itemsListIndexBefore).to.be.equal(0);
322 const args = [{NFT: {}},
323 {NFT: {}},178 name: 'name',
324 {NFT: {}}];179 description: 'descr',
325 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
326 await expect(executeTransaction(api, bob, createMultipleItemsTx)).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);180 tokenPrefix: 'COL',
327 });181 });
182 const args = [
183 {},
184 {},
185 ];
186 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
187 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
328 });188 });
329189
330 it('Regular user cannot create items in active Fungible collection', async () => {190 itSub('Regular user cannot create items in active Fungible collection', async ({helper}) => {
331 await usingApi(async (api: ApiPromise) => {
332 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});191 const collection = await helper.ft.mintCollection(alice, {
333 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
334 expect(itemsListIndexBefore).to.be.equal(0);
335 const args = [
336 {Fungible: {value: 1}},192 name: 'name',
337 {Fungible: {value: 2}},193 description: 'descr',
338 {Fungible: {value: 3}},194 tokenPrefix: 'COL',
339 ];
340 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
341 await expect(executeTransaction(api, bob, createMultipleItemsTx)).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
342 });195 });
196 const args = [
197 {value: 1n},
198 {value: 2n},
199 {value: 3n},
200 ];
201 const mintTx = async () => helper.ft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, args, {Substrate: alice.address});
202 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
343 });203 });
344204
345 it('Regular user cannot create items in active ReFungible collection', async function() {205 itSub.ifWithPallets('Regular user cannot create items in active ReFungible collection', [Pallets.ReFungible], async ({helper}) => {
346 await requirePallets(this, [Pallets.ReFungible]);
347
348 await usingApi(async (api: ApiPromise) => {
349 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});206 const collection = await helper.rft.mintCollection(alice, {
350 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
351 expect(itemsListIndexBefore).to.be.equal(0);
352 const args = [
353 {ReFungible: {pieces: 1}},207 name: 'name',
354 {ReFungible: {pieces: 1}},208 description: 'descr',
355 {ReFungible: {pieces: 1}},209 tokenPrefix: 'COL',
356 ];
357 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
358 await expect(executeTransaction(api, bob, createMultipleItemsTx)).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
359 });210 });
211 const args = [
212 {pieces: 1n},
213 {pieces: 1n},
214 {pieces: 1n},
215 ];
216 const mintTx = async () => helper.rft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);
217 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);
360 });218 });
361219
362 it('Create token in not existing collection', async () => {220 itSub('Create token in not existing collection', async ({helper}) => {
363 await usingApi(async (api: ApiPromise) => {221 const collectionId = 1_000_000;
222 const args = [
364 const collectionId = await getCreatedCollectionCount(api) + 1;223 {},
224 {},
365 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), ['NFT', 'NFT', 'NFT']);225 ];
226 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(bob, collectionId, {Substrate: alice.address}, args);
366 await expect(executeTransaction(api, alice, createMultipleItemsTx)).to.be.rejectedWith(/common\.CollectionNotFound/);227 await expect(mintTx()).to.be.rejectedWith(/common\.CollectionNotFound/);
367 });
368 });228 });
369229
370 it('Create NFTs that has reached the maximum data limit', async function() {230 itSub('Create NFTs that has reached the maximum data limit', async ({helper}) => {
371 await usingApi(async (api, privateKeyWrapper) => {
372 const collectionId = await createCollectionWithPropsExpectSuccess({231 const collection = await helper.nft.mintCollection(alice, {
373 propPerm: [{key: 'key', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}],232 name: 'name',
233 description: 'descr',
374 });234 tokenPrefix: 'COL',
375 const alice = privateKeyWrapper('//Alice');
376 const args = [235 tokenPropertyPermissions: [
377 {NFT: {properties: [{key: 'key', value: 'A'.repeat(32769)}]}},236 {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
378 {NFT: {properties: [{key: 'key', value: 'B'.repeat(32769)}]}},
379 {NFT: {properties: [{key: 'key', value: 'C'.repeat(32769)}]}},
380 ];237 ],
381 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
382 await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTx)).to.be.rejected;
383 });238 });
239 const args = [
240 {properties: [{key: 'data', value: 'A'.repeat(32769)}]},
241 {properties: [{key: 'data', value: 'B'.repeat(32769)}]},
242 {properties: [{key: 'data', value: 'C'.repeat(32769)}]},
243 ];
244 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
245 await expect(mintTx()).to.be.rejectedWith('Verification Error');
384 });246 });
385247
386 it('Create Refungible tokens that has reached the maximum data limit', async function() {248 itSub.ifWithPallets('Create Refungible tokens that has reached the maximum data limit', [Pallets.ReFungible], async ({helper}) => {
387 await requirePallets(this, [Pallets.ReFungible]);
388
389 await usingApi(async api => {
390 const collectionIdReFungible =249 const collection = await helper.rft.mintCollection(alice, {
391 await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
392 {250 name: 'name',
393 const argsReFungible = [
394 {ReFungible: [10, [['key', 'A'.repeat(32769)]]]},
395 {ReFungible: [10, [['key', 'B'.repeat(32769)]]]},251 description: 'descr',
396 {ReFungible: [10, [['key', 'C'.repeat(32769)]]]},252 tokenPrefix: 'COL',
397 ];253 tokenPropertyPermissions: [
398 const createMultipleItemsTxFungible = api.tx.unique
399 .createMultipleItems(collectionIdReFungible, normalizeAccountId(alice.address), argsReFungible);
400 await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTxFungible)).to.be.rejected;
401 }
402 {
403 const argsReFungible = [
404 {ReFungible: {properties: [{key: 'key', value: 'A'.repeat(32769)}]}},254 {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
405 {ReFungible: {properties: [{key: 'key', value: 'B'.repeat(32769)}]}},
406 {ReFungible: {properties: [{key: 'key', value: 'C'.repeat(32769)}]}},
407 ];255 ],
408 const createMultipleItemsTxFungible = api.tx.unique
409 .createMultipleItems(collectionIdReFungible, normalizeAccountId(alice.address), argsReFungible);
410 await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTxFungible)).to.be.rejected;
411 }
412 });256 });
257 const args = [
258 {pieces: 10n, properties: [{key: 'data', value: 'A'.repeat(32769)}]},
259 {pieces: 10n, properties: [{key: 'data', value: 'B'.repeat(32769)}]},
260 {pieces: 10n, properties: [{key: 'data', value: 'C'.repeat(32769)}]},
261 ];
262 const mintTx = async () => helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
263 await expect(mintTx()).to.be.rejectedWith('Verification Error');
413 });264 });
414265
415 it('Create tokens with different types', async () => {266 itSub.ifWithPallets('Create tokens with different types', [Pallets.ReFungible], async ({helper}) => {
416 await usingApi(async (api: ApiPromise) => {
417 const collectionId = await createCollectionExpectSuccess();267 const {collectionId} = await helper.nft.mintCollection(alice, {
418
419 const types = ['NFT', 'Fungible'];
420
421 if (await checkPalletsPresence([Pallets.ReFungible])) {
422 types.push('ReFungible');268 name: 'name',
423 }
424
425 const createMultipleItemsTx = api.tx.unique
426 .createMultipleItems(collectionId, normalizeAccountId(alice.address), types);
427 await expect(executeTransaction(api, alice, createMultipleItemsTx)).to.be.rejectedWith(/nonfungible\.NotNonfungibleDataUsedToMintFungibleCollectionToken/);269 description: 'descr',
428 // garbage collection :-D // lol270 tokenPrefix: 'COL',
429 await destroyCollectionExpectSuccess(collectionId);
430 });271 });
272
273 const types = ['NFT', 'Fungible', 'ReFungible'];
274 await expect(helper.executeExtrinsic(
275 alice,
276 'api.tx.unique.createMultipleItems',
277 [collectionId, {Substrate: alice.address}, types],
278 )).to.be.rejectedWith(/nonfungible\.NotNonfungibleDataUsedToMintFungibleCollectionToken/);
431 });279 });
432280
433 it('Create tokens with different data limits <> maximum data limit', async () => {281 itSub('Create tokens with different data limits <> maximum data limit', async ({helper}) => {
434 await usingApi(async (api: ApiPromise) => {
435 const collectionId = await createCollectionWithPropsExpectSuccess({282 const collection = await helper.nft.mintCollection(alice, {
436 propPerm: [{key: 'key', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}],283 name: 'name',
284 description: 'descr',
437 });285 tokenPrefix: 'COL',
438 const args = [286 tokenPropertyPermissions: [
439 {NFT: {properties: [{key: 'key', value: 'A'}]}},287 {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
440 {NFT: {properties: [{key: 'key', value: 'B'.repeat(32769)}]}},
441 ];288 ],
442 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
443 await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTx)).to.be.rejected;
444 });289 });
290 const args = [
291 {properties: [{key: 'data', value: 'A'}]},
292 {properties: [{key: 'data', value: 'B'.repeat(32769)}]},
293 ];
294 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
295 await expect(mintTx()).to.be.rejectedWith('Verification Error');
445 });296 });
446297
447 it('Fails when minting tokens exceeds collectionLimits amount', async () => {298 itSub('Fails when minting tokens exceeds collectionLimits amount', async ({helper}) => {
448 await usingApi(async (api) => {299 const collection = await helper.nft.mintCollection(alice, {
300 name: 'name',
449 const collectionId = await createCollectionExpectSuccess();301 description: 'descr',
302 tokenPrefix: 'COL',
450 await setCollectionLimitsExpectSuccess(alice, collectionId, {303 tokenPropertyPermissions: [
304 {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},
305 ],
306 limits: {
451 tokenLimit: 1,307 tokenLimit: 1,
452 });308 },
453 const args = [
454 {NFT: {}},
455 {NFT: {}},
456 ];
457 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
458 await expect(executeTransaction(api, alice, createMultipleItemsTx)).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
459 });309 });
310 const args = [
311 {},
312 {},
313 ];
314 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
315 await expect(mintTx()).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);
460 });316 });
461317
462 it('User doesnt have editing rights', async () => {318 itSub('User doesnt have editing rights', async ({helper}) => {
463 await usingApi(async (api: ApiPromise) => {
464 const collectionId = await createCollectionWithPropsExpectSuccess({319 const collection = await helper.nft.mintCollection(alice, {
465 propPerm: [{key: 'key1', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}],320 name: 'name',
321 description: 'descr',
466 });322 tokenPrefix: 'COL',
467 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
468 expect(itemsListIndexBefore).to.be.equal(0);323 tokenPropertyPermissions: [
469 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
470 const args = [
471 {NFT: {properties: [{key: 'key1', value: 'v2'}]}},324 {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: false}},
472 {NFT: {}},
473 {NFT: {}},
474 ];325 ],
475
476 const tx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(bob.address), args);
477 await expect(executeTransaction(api, bob, tx)).to.be.rejectedWith(/common\.NoPermission/);
478 });326 });
327 const args = [
328 {properties: [{key: 'data', value: 'A'}]},
329 ];
330 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
331 await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
479 });332 });
480333
481 it('Adding property without access rights', async () => {334 itSub('Adding property without access rights', async ({helper}) => {
482 await usingApi(async (api: ApiPromise) => {335 const collection = await helper.nft.mintCollection(alice, {
483 const collectionId = await createCollectionWithPropsExpectSuccess({properties: [{key: 'k', value: 'v1'}]});336 name: 'name',
337 description: 'descr',
484 const itemsListIndexBefore = await getLastTokenId(api, collectionId);338 tokenPrefix: 'COL',
485 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);339 properties: [
486 expect(itemsListIndexBefore).to.be.equal(0);340 {
487 const args = [{NFT: {properties: [{key: 'k', value: 'v'}]}},341 key: 'data',
488 {NFT: {}},342 value: 'v',
489 {NFT: {}}];343 },
490
491 const tx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(bob.address), args);
492 await expect(executeTransaction(api, bob, tx)).to.be.rejectedWith(/common\.NoPermission/);344 ],
493 });345 });
346 const args = [
347 {properties: [{key: 'data', value: 'A'}]},
348 ];
349 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
350 await expect(mintTx()).to.be.rejectedWith(/common\.NoPermission/);
494 });351 });
495352
496 it('Adding more than 64 prps', async () => {353 itSub('Adding more than 64 prps', async ({helper}) => {
497 await usingApi(async (api: ApiPromise) => {
498 const propPerms = [{key: 'key', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}];354 const collection = await helper.nft.mintCollection(alice, {
499 for (let i = 0; i < 65; i++) {
500 propPerms.push({key: `key${i}`, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}});
501 }
502
503 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
504
505 const tx1 = api.tx.unique.setTokenPropertyPermissions(collectionId, propPerms);
506 await expect(executeTransaction(api, alice, tx1)).to.be.rejectedWith(/common\.PropertyLimitReached/);
507
508 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
509 expect(itemsListIndexBefore).to.be.equal(0);
510 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);
511
512 const prps = [];
513
514 for (let i = 0; i < 65; i++) {
515 prps.push({key: `key${i}`, value: `value${i}`});355 name: 'name',
516 }356 description: 'descr',
517
518 const args = [
519 {NFT: {properties: prps}},
520 {NFT: {properties: prps}},357 tokenPrefix: 'COL',
521 {NFT: {properties: prps}},
522 ];
523
524 // there are no permissions, but will fail anyway because of too much weight for a block
525 const tx2 = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);
526 await expect(submitTransactionExpectFailAsync(alice, tx2)).to.be.rejected;
527 });358 });
528 });359 const prps = [];
529360
530 it('Trying to add bigger property than allowed', async () => {361 for (let i = 0; i < 65; i++) {
531 await usingApi(async (api: ApiPromise) => {
532 const collectionId = await createCollectionWithPropsExpectSuccess({362 prps.push({key: `key${i}`, value: `value${i}`});
533 propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}],
534 });
535 const itemsListIndexBefore = await getLastTokenId(api, collectionId);
536 expect(itemsListIndexBefore).to.be.equal(0);
537 const args = [{NFT: {properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]}},
538 {NFT: {properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]}},
539 {NFT: {properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]}}];363 }
540364
541 const tx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);365 const args = [
366 {properties: prps},
367 {properties: prps},
368 {properties: prps},
369 ];
370
371 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);
542 await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);372 await expect(mintTx()).to.be.rejectedWith('Verification Error');
543 });
544 });373 });
545});374});
546375
modifiedtests/src/createMultipleItemsEx.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {expect} from 'chai';17import {IKeyringPair} from '@polkadot/types/types';
18import usingApi, {executeTransaction} from './substrate/substrate-api';18import {usingPlaygrounds, expect, Pallets, itSub} from './util/playgrounds';
19import {addCollectionAdminExpectSuccess, createCollectionExpectSuccess, createCollectionWithPropsExpectSuccess, getBalance, getLastTokenId, getTokenProperties, requirePallets, Pallets} from './util/helpers';19import {IProperty} from './util/playgrounds/types';
2020
21describe('Integration Test: createMultipleItemsEx', () => {21describe('Integration Test: createMultipleItemsEx', () => {
22 it('can initialize multiple NFT with different owners', async () => {22 let alice: IKeyringPair;
23 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
24 await usingApi(async (api, privateKeyWrapper) => {23 let bob: IKeyringPair;
25 const alice = privateKeyWrapper('//Alice');
26 const bob = privateKeyWrapper('//Bob');
27 const charlie = privateKeyWrapper('//Charlie');24 let charlie: IKeyringPair;
28 const data = [
29 {
30 owner: {substrate: alice.address},
31 }, {
32 owner: {substrate: bob.address},
33 }, {
34 owner: {substrate: charlie.address},
35 },
36 ];
3725
38 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {26 before(async () => {
39 NFT: data,27 await usingPlaygrounds(async (helper, privateKey) => {
40 }));
41 const tokens = await api.query.nonfungible.tokenData.entries(collection);28 const donor = privateKey('//Alice');
42 const json = tokens.map(([, token]) => token.toJSON());29 [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
43 expect(json).to.be.deep.equal(data);
44 });30 });
45 });31 });
4632
47 it('createMultipleItemsEx with property Admin', async () => {33 itSub('can initialize multiple NFT with different owners', async ({helper}) => {
48 const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: false}}]});34 const collection = await helper.nft.mintCollection(alice, {
35 name: 'name',
36 description: 'descr',
37 tokenPrefix: 'COL',
38 });
39 const args = [
40 {
41 owner: {Substrate: alice.address},
42 },
43 {
44 owner: {Substrate: bob.address},
45 },
46 {
47 owner: {Substrate: charlie.address},
48 },
49 ];
50
49 await usingApi(async (api, privateKeyWrapper) => {51 const tokens = await collection.mintMultipleTokens(alice, args);
52 for (const [i, token] of tokens.entries()) {
50 const alice = privateKeyWrapper('//Alice');53 expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
54 }
55 });
56
51 const bob = privateKeyWrapper('//Bob');57 itSub('createMultipleItemsEx with property Admin', async ({helper}) => {
58 const collection = await helper.nft.mintCollection(alice, {
59 name: 'name',
52 const charlie = privateKeyWrapper('//Charlie');60 description: 'descr',
61 tokenPrefix: 'COL',
53 const data = [62 tokenPropertyPermissions: [
54 {63 {
55 owner: {substrate: alice.address},64 key: 'k',
56 properties: [{key: 'k', value: 'v1'}],65 permission: {
57 }, {
58 owner: {substrate: bob.address},66 mutable: true,
59 properties: [{key: 'k', value: 'v2'}],67 collectionAdmin: true,
60 }, {68 tokenOwner: false,
61 owner: {substrate: charlie.address},
62 properties: [{key: 'k', value: 'v3'}],69 },
63 },70 },
64 ];71 ],
72 });
6573
66 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {74 const args = [
75 {
76 owner: {Substrate: alice.address},
77 properties: [{key: 'k', value: 'v1'}],
78 },
79 {
80 owner: {Substrate: bob.address},
81 properties: [{key: 'k', value: 'v2'}],
67 NFT: data,82 },
83 {
84 owner: {Substrate: charlie.address},
68 }));85 properties: [{key: 'k', value: 'v3'}],
69 for (let i = 1; i < 4; i++) {86 },
87 ];
88
89 const tokens = await collection.mintMultipleTokens(alice, args);
90 for (const [i, token] of tokens.entries()) {
70 expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;91 expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
71 }92 expect(await token.getData()).to.not.be.empty;
72 });93 }
73 });94 });
7495
75 it('createMultipleItemsEx with property AdminConst', async () => {96 itSub('createMultipleItemsEx with property AdminConst', async ({helper}) => {
76 const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: false}}]});97 const collection = await helper.nft.mintCollection(alice, {
77 await usingApi(async (api, privateKeyWrapper) => {
78 const alice = privateKeyWrapper('//Alice');98 name: 'name',
79 const bob = privateKeyWrapper('//Bob');99 description: 'descr',
80 const charlie = privateKeyWrapper('//Charlie');100 tokenPrefix: 'COL',
81 const data = [101 tokenPropertyPermissions: [
82 {102 {
83 owner: {substrate: alice.address},103 key: 'k',
84 properties: [{key: 'k', value: 'v1'}],104 permission: {
85 }, {
86 owner: {substrate: bob.address},105 mutable: false,
87 properties: [{key: 'k', value: 'v2'}],106 collectionAdmin: true,
88 }, {107 tokenOwner: false,
89 owner: {substrate: charlie.address},
90 properties: [{key: 'k', value: 'v3'}],108 },
91 },109 },
92 ];110 ],
111 });
93112
94 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {113 const args = [
114 {
115 owner: {Substrate: alice.address},
116 properties: [{key: 'k', value: 'v1'}],
117 },
118 {
119 owner: {Substrate: bob.address},
120 properties: [{key: 'k', value: 'v2'}],
95 NFT: data,121 },
122 {
123 owner: {Substrate: charlie.address},
96 }));124 properties: [{key: 'k', value: 'v3'}],
97 for (let i = 1; i < 4; i++) {125 },
126 ];
127
128 const tokens = await collection.mintMultipleTokens(alice, args);
129 for (const [i, token] of tokens.entries()) {
98 expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;130 expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
99 }131 expect(await token.getData()).to.not.be.empty;
100 });132 }
101 });133 });
102134
103 it('createMultipleItemsEx with property itemOwnerOrAdmin', async () => {135 itSub('createMultipleItemsEx with property itemOwnerOrAdmin', async ({helper}) => {
104 const collection = await createCollectionWithPropsExpectSuccess({mode: {type: 'NFT'}, propPerm: [{key: 'k', permission: {mutable: false, collectionAdmin: true, tokenOwner: true}}]});136 const collection = await helper.nft.mintCollection(alice, {
105 await usingApi(async (api, privateKeyWrapper) => {
106 const alice = privateKeyWrapper('//Alice');137 name: 'name',
107 const bob = privateKeyWrapper('//Bob');138 description: 'descr',
108 const charlie = privateKeyWrapper('//Charlie');139 tokenPrefix: 'COL',
109 const data = [140 tokenPropertyPermissions: [
110 {141 {
111 owner: {substrate: alice.address},142 key: 'k',
112 properties: [{key: 'k', value: 'v1'}],143 permission: {
113 }, {
114 owner: {substrate: bob.address},144 mutable: false,
115 properties: [{key: 'k', value: 'v2'}],145 collectionAdmin: true,
116 }, {146 tokenOwner: true,
117 owner: {substrate: charlie.address},
118 properties: [{key: 'k', value: 'v3'}],147 },
119 },148 },
120 ];149 ],
150 });
121151
122 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {152 const args = [
153 {
154 owner: {Substrate: alice.address},
155 properties: [{key: 'k', value: 'v1'}],
156 },
157 {
158 owner: {Substrate: bob.address},
159 properties: [{key: 'k', value: 'v2'}],
123 NFT: data,160 },
161 {
162 owner: {Substrate: charlie.address},
124 }));163 properties: [{key: 'k', value: 'v3'}],
125 for (let i = 1; i < 4; i++) {164 },
165 ];
166
167 const tokens = await collection.mintMultipleTokens(alice, args);
168 for (const [i, token] of tokens.entries()) {
126 expect(await api.rpc.unique.tokenProperties(collection, i)).not.to.be.empty;169 expect(await token.getOwner()).to.be.deep.equal(args[i].owner);
127 }170 expect(await token.getData()).to.not.be.empty;
128 });171 }
129 });172 });
130173
131 it('can initialize fungible with multiple owners', async () => {174 itSub('can initialize fungible with multiple owners', async ({helper}) => {
132 const collection = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});175 const collection = await helper.ft.mintCollection(alice, {
176 name: 'name',
133 await usingApi(async (api, privateKeyWrapper) => {177 description: 'descr',
134 const alice = privateKeyWrapper('//Alice');178 tokenPrefix: 'COL',
135 const bob = privateKeyWrapper('//Bob');179 }, 0);
136180
137 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {181 const api = helper.api;
182 await helper.signTransaction(alice, api?.tx.unique.createMultipleItemsEx(collection.collectionId, {
138 Fungible: new Map([183 Fungible: new Map([
139 [JSON.stringify({Substrate: alice.address}), 50],184 [JSON.stringify({Substrate: alice.address}), 50],
140 [JSON.stringify({Substrate: bob.address}), 100],185 [JSON.stringify({Substrate: bob.address}), 100],
141 ]),186 ]),
142 }));187 }));
143188
144 expect(await getBalance(api, collection, alice.address, 0)).to.equal(50n);189 expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(50n);
145 expect(await getBalance(api, collection, bob.address, 0)).to.equal(100n);190 expect(await collection.getBalance({Substrate: bob.address})).to.be.equal(100n);
191 });
192
193 itSub.ifWithPallets('can initialize an RFT with multiple owners', [Pallets.ReFungible], async ({helper}) => {
194 const collection = await helper.rft.mintCollection(alice, {
195 name: 'name',
196 description: 'descr',
197 tokenPrefix: 'COL',
198 tokenPropertyPermissions: [
199 {key: 'k', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}},
200 ],
146 });201 });
202
203 const api = helper.api;
204 await helper.signTransaction(alice, api?.tx.unique.createMultipleItemsEx(collection.collectionId, {
205 RefungibleMultipleOwners: {
206 users: new Map([
207 [JSON.stringify({Substrate: alice.address}), 1],
208 [JSON.stringify({Substrate: bob.address}), 2],
209 ]),
210 properties: [
211 {key: 'k', value: 'v'},
212 ],
213 },
214 }));
215 const tokenId = await collection.getLastTokenId();
216 expect(tokenId).to.be.equal(1);
217 expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n);
218 expect(await collection.getTokenBalance(1, {Substrate: bob.address})).to.be.equal(2n);
147 });219 });
148220
149 it('can initialize an RFT with multiple owners', async function() {221 itSub.ifWithPallets('can initialize multiple RFTs with the same owner', [Pallets.ReFungible], async ({helper}) => {
150 await requirePallets(this, [Pallets.ReFungible]);222 const collection = await helper.rft.mintCollection(alice, {
223 name: 'name',
224 description: 'descr',
225 tokenPrefix: 'COL',
226 tokenPropertyPermissions: [
227 {key: 'k', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}},
228 ],
229 });
151230
152 await usingApi(async (api, privateKeyWrapper) => {231 const api = helper.api;
153 const alice = privateKeyWrapper('//Alice');
154 const bob = privateKeyWrapper('//Bob');
155 const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
156 await executeTransaction(
157 api,
158 alice,
159 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'data', permission: {tokenOwner: true}}]),
160 );
161232
162 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {233 await helper.signTransaction(alice, api?.tx.unique.createMultipleItemsEx(collection.collectionId, {
163 RefungibleMultipleOwners: {234 RefungibleMultipleItems: [
164 users: new Map([235 {
165 [JSON.stringify({Substrate: alice.address}), 1],236 user: {Substrate: alice.address}, pieces: 1,
166 [JSON.stringify({Substrate: bob.address}), 2],
167 ]),
168 properties: [237 properties: [
169 {key: 'data', value: 'testValue'},238 {key: 'k', value: 'v1'},
170 ],239 ],
171 },240 },
172 }));241 {
242 user: {Substrate: alice.address}, pieces: 3,
243 properties: [
244 {key: 'k', value: 'v2'},
245 ],
246 },
247 ],
248 }));
173249
174 const itemsListIndexAfter = await getLastTokenId(api, collection);250 expect(await collection.getLastTokenId()).to.be.equal(2);
251 expect(await collection.getTokenBalance(1, {Substrate: alice.address})).to.be.equal(1n);
175 expect(itemsListIndexAfter).to.be.equal(1);252 expect(await collection.getTokenBalance(2, {Substrate: alice.address})).to.be.equal(3n);
176253
177 expect(await getBalance(api, collection, alice.address, 1)).to.be.equal(1n);254 const tokenData1 = await helper.rft.getToken(collection.collectionId, 1);
178 expect(await getBalance(api, collection, bob.address, 1)).to.be.equal(2n);255 expect(tokenData1).to.not.be.null;
179 expect((await getTokenProperties(api, collection, 1, ['data']))[0].value).to.be.equal('testValue');256 expect(tokenData1?.properties[0]).to.be.deep.equal({key: 'k', value: 'v1'});
180 });
181 });
182257
183 it('can initialize multiple RFTs with the same owner', async function() {258 const tokenData2 = await helper.rft.getToken(collection.collectionId, 2);
184 await requirePallets(this, [Pallets.ReFungible]);
185
186 await usingApi(async (api, privateKeyWrapper) => {
187 const alice = privateKeyWrapper('//Alice');
188 const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});
189 await executeTransaction(
190 api,
191 alice,
192 api.tx.unique.setTokenPropertyPermissions(collection, [{key: 'data', permission: {tokenOwner: true}}]),
193 );
194
195 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {
196 RefungibleMultipleItems: [
197 {
198 user: {Substrate: alice.address}, pieces: 1,
199 properties: [
200 {key: 'data', value: 'testValue1'},
201 ],
202 },
203 {
204 user: {Substrate: alice.address}, pieces: 3,
205 properties: [
206 {key: 'data', value: 'testValue2'},
207 ],
208 },
209 ],
210 }));
211
212 const itemsListIndexAfter = await getLastTokenId(api, collection);
213 expect(itemsListIndexAfter).to.be.equal(2);
214
215 expect(await getBalance(api, collection, alice.address, 1)).to.be.equal(1n);259 expect(tokenData2).to.not.be.null;
216 expect(await getBalance(api, collection, alice.address, 2)).to.be.equal(3n);
217 expect((await getTokenProperties(api, collection, 1, ['data']))[0].value).to.be.equal('testValue1');260 expect(tokenData2?.properties[0]).to.be.deep.equal({key: 'k', value: 'v2'});
218 expect((await getTokenProperties(api, collection, 2, ['data']))[0].value).to.be.equal('testValue2');
219 });
220 });261 });
221});262});
222263
223describe('Negative test: createMultipleItemsEx', () => {264describe('Negative test: createMultipleItemsEx', () => {
224 it('No editing rights', async () => {265 let alice: IKeyringPair;
225 const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],
226 propPerm: [{key: 'key1', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}]});
227 await usingApi(async (api, privateKeyWrapper) => {266 let bob: IKeyringPair;
228 const alice = privateKeyWrapper('//Alice');
229 const bob = privateKeyWrapper('//Bob');
230 const charlie = privateKeyWrapper('//Charlie');267 let charlie: IKeyringPair;
231 await addCollectionAdminExpectSuccess(alice, collection, bob.address);
232 const data = [
233 {
234 owner: {substrate: alice.address},
235 properties: [{key: 'key1', value: 'v2'}],
236 }, {
237 owner: {substrate: bob.address},
238 properties: [{key: 'key1', value: 'v2'}],
239 }, {
240 owner: {substrate: charlie.address},
241 properties: [{key: 'key1', value: 'v2'}],
242 },
243 ];
244268
245 const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});269 before(async () => {
246 // await executeTransaction(api, alice, tx);270 await usingPlaygrounds(async (helper, privateKey) => {
247271 const donor = privateKey('//Alice');
248 //await submitTransactionExpectFailAsync(alice, tx);272 [alice, bob, charlie] = await helper.arrange.createAccounts([100n, 100n, 100n], donor);
249 await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);
250 });273 });
251 });274 });
252275
253 it('User doesnt have editing rights', async () => {276 itSub('No editing rights', async ({helper}) => {
254 const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}],277 const collection = await helper.nft.mintCollection(alice, {
255 propPerm: [{key: 'key1', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}]});278 name: 'name',
256 await usingApi(async (api, privateKeyWrapper) => {
257 const alice = privateKeyWrapper('//Alice');279 description: 'descr',
258 const bob = privateKeyWrapper('//Bob');280 tokenPrefix: 'COL',
259 await addCollectionAdminExpectSuccess(alice, collection, bob.address);
260 const data = [281 tokenPropertyPermissions: [
261 {282 {
262 owner: {substrate: alice.address},283 key: 'k',
263 properties: [{key: 'key1', value: 'v2'}],284 permission: {
264 }, {285 mutable: true,
265 owner: {substrate: alice.address},
266 properties: [{key: 'key1', value: 'v2'}],286 collectionAdmin: false,
267 }, {287 tokenOwner: false,
268 owner: {substrate: alice.address},
269 properties: [{key: 'key1', value: 'v2'}],288 },
270 },289 },
271 ];290 ],
291 });
272292
273 const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});293 const args = [
294 {
295 owner: {Substrate: alice.address},
296 properties: [{key: 'k', value: 'v1'}],
297 },
298 {
299 owner: {Substrate: bob.address},
300 properties: [{key: 'k', value: 'v2'}],
274 // await executeTransaction(api, alice, tx);301 },
302 {
303 owner: {Substrate: charlie.address},
304 properties: [{key: 'k', value: 'v3'}],
305 },
306 ];
275307
276 //await submitTransactionExpectFailAsync(alice, tx);308 await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
277 await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);
278 });
279 });309 });
280310
281 it('Adding property without access rights', async () => {311 itSub('User doesnt have editing rights', async ({helper}) => {
282 const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}]});312 const collection = await helper.nft.mintCollection(alice, {
283 await usingApi(async (api, privateKeyWrapper) => {
284 const alice = privateKeyWrapper('//Alice');313 name: 'name',
285 const bob = privateKeyWrapper('//Bob');314 description: 'descr',
286 const charlie = privateKeyWrapper('//Charlie');315 tokenPrefix: 'COL',
287 await addCollectionAdminExpectSuccess(alice, collection, bob.address);
288 const data = [316 tokenPropertyPermissions: [
289 {317 {
290 owner: {substrate: alice.address},318 key: 'k',
291 properties: [{key: 'key1', value: 'v2'}],319 permission: {
292 }, {320 mutable: false,
293 owner: {substrate: bob.address},
294 properties: [{key: 'key1', value: 'v2'}],321 collectionAdmin: false,
295 }, {322 tokenOwner: false,
296 owner: {substrate: charlie.address},
297 properties: [{key: 'key1', value: 'v2'}],323 },
298 },324 },
299 ];325 ],
326 });
300327
301 const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});328 const args = [
329 {
330 owner: {Substrate: alice.address},
331 properties: [{key: 'k', value: 'v1'}],
332 },
333 {
334 owner: {Substrate: bob.address},
335 properties: [{key: 'k', value: 'v2'}],
336 },
337 {
338 owner: {Substrate: charlie.address},
339 properties: [{key: 'k', value: 'v3'}],
340 },
341 ];
302342
303 await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);343 await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
304 //await submitTransactionExpectFailAsync(alice, tx);
305 });
306 });344 });
307345
308 it('Adding more than 64 properties', async () => {346 itSub('Adding property without access rights', async ({helper}) => {
309 const propPerms = [{key: 'key', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}];347 const collection = await helper.nft.mintCollection(alice, {
348 name: 'name',
349 description: 'descr',
350 tokenPrefix: 'COL',
351 });
310352
311 for (let i = 0; i < 65; i++) {353 const args = [
354 {
312 propPerms.push({key: `key${i}`, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}});355 owner: {Substrate: alice.address},
356 properties: [{key: 'k', value: 'v1'}],
357 },
358 {
359 owner: {Substrate: bob.address},
360 properties: [{key: 'k', value: 'v2'}],
361 },
362 {
363 owner: {Substrate: charlie.address},
364 properties: [{key: 'k', value: 'v3'}],
313 }365 },
366 ];
314367
315 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});368 await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith(/common\.NoPermission/);
316 await usingApi(async (api, privateKeyWrapper) => {
317 const alice = privateKeyWrapper('//Alice');
318 await expect(executeTransaction(api, alice, api.tx.unique.setTokenPropertyPermissions(collection, propPerms))).to.be.rejectedWith(/common\.PropertyLimitReached/);
319 });
320 });369 });
321370
322 it('Trying to add bigger property than allowed', async () => {371 itSub('Adding more than 64 properties', async ({helper}) => {
323 const collection = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]});372 const collection = await helper.nft.mintCollection(alice, {
324 await usingApi(async (api, privateKeyWrapper) => {373 name: 'name',
325 const alice = privateKeyWrapper('//Alice');
326 const bob = privateKeyWrapper('//Bob');374 description: 'descr',
327 const charlie = privateKeyWrapper('//Charlie');375 tokenPrefix: 'COL',
328 await addCollectionAdminExpectSuccess(alice, collection, bob.address);
329 const data = [376 tokenPropertyPermissions: [
330 {377 {
331 owner: {substrate: alice.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],378 key: 'k',
332 }, {379 permission: {
333 owner: {substrate: bob.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],380 mutable: true,
334 }, {381 collectionAdmin: true,
335 owner: {substrate: charlie.address}, properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}],382 tokenOwner: true,
383 },
336 },384 },
337 ];385 ],
386 });
338387
339 const tx = api.tx.unique.createMultipleItemsEx(collection, {NFT: data});388 const properties: IProperty[] = [];
340389
341 //await submitTransactionExpectFailAsync(alice, tx);390 for (let i = 0; i < 65; i++) {
342 await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);391 properties.push({key: `k${i}`, value: `v${i}`});
343 });
344 });392 }
345393
346 it('can initialize multiple NFT with different owners', async () => {394 const args = [
395 {
347 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});396 owner: {Substrate: alice.address},
348 await usingApi(async (api, privateKeyWrapper) => {
349 const alice = privateKeyWrapper('//Alice');397 properties: properties,
350 const bob = privateKeyWrapper('//Bob');398 },
351 const charlie = privateKeyWrapper('//Charlie');399 {
352 const data = [
353 {
354 owner: {substrate: alice.address},400 owner: {Substrate: bob.address},
355 }, {401 properties: properties,
356 owner: {substrate: bob.address},402 },
357 }, {403 {
358 owner: {substrate: charlie.address},404 owner: {Substrate: charlie.address},
359 },405 properties: properties,
360 ];406 },
407 ];
361408
362 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {409 await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error');
363 NFT: data,
364 }));
365 const tokens = await api.query.nonfungible.tokenData.entries(collection);
366 const json = tokens.map(([, token]) => token.toJSON());
367 expect(json).to.be.deep.equal(data);
368 });
369 });410 });
370411
371 it('can initialize multiple NFT with different owners', async () => {412 itSub('Trying to add bigger property than allowed', async ({helper}) => {
372 const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});413 const collection = await helper.nft.mintCollection(alice, {
373 await usingApi(async (api, privateKeyWrapper) => {
374 const alice = privateKeyWrapper('//Alice');414 name: 'name',
375 const bob = privateKeyWrapper('//Bob');415 description: 'descr',
376 const charlie = privateKeyWrapper('//Charlie');416 tokenPrefix: 'COL',
377 const data = [417 tokenPropertyPermissions: [
378 {418 {
379 owner: {substrate: alice.address},419 key: 'k',
380 }, {420 permission: {
381 owner: {substrate: bob.address},421 mutable: true,
382 }, {422 collectionAdmin: true,
383 owner: {substrate: charlie.address},423 tokenOwner: true,
424 },
384 },425 },
385 ];426 ],
427 });
386428
387 await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, {429 const args = [
430 {
431 owner: {Substrate: alice.address},
432 properties: [{key: 'k', value: 'A'.repeat(32769)}],
433 },
434 {
388 NFT: data,435 owner: {Substrate: bob.address},
389 }));436 properties: [{key: 'k', value: 'A'.repeat(32769)}],
390 const tokens = await api.query.nonfungible.tokenData.entries(collection);437 },
438 {
439 owner: {Substrate: charlie.address},
391 const json = tokens.map(([, token]) => token.toJSON());440 properties: [{key: 'k', value: 'A'.repeat(32769)}],
441 },
442 ];
443
392 expect(json).to.be.deep.equal(data);444 await expect(collection.mintMultipleTokens(alice, args)).to.be.rejectedWith('Verification Error');
393 });
394 });445 });
395});446});
396447
modifiedtests/src/creditFeesToTreasury.test.tsdiffbeforeafterboth
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import './interfaces/augment-api-consts';17import './interfaces/augment-api-consts';
18import chai from 'chai';
19import chaiAsPromised from 'chai-as-promised';
20import {default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync} from './substrate/substrate-api';
21import {IKeyringPair} from '@polkadot/types/types';18import {IKeyringPair} from '@polkadot/types/types';
22import {
23 createCollectionExpectSuccess,
24 createItemExpectSuccess,
25 getGenericResult,
26 transferExpectSuccess,
27 UNIQUE,
28} from './util/helpers';
29
30import {default as waitNewBlocks} from './substrate/wait-new-blocks';
31import {ApiPromise} from '@polkadot/api';19import {ApiPromise} from '@polkadot/api';
32
33chai.use(chaiAsPromised);
34const expect = chai.expect;20import {usingPlaygrounds, expect, itSub} from './util/playgrounds';
3521
36const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z';22const TREASURY = '5EYCAe5ijiYfyeZ2JJCGq56LmPyNRAKzpG4QkoQkkQNB5e6Z';
37const saneMinimumFee = 0.05;23const saneMinimumFee = 0.05;
38const saneMaximumFee = 0.5;24const saneMaximumFee = 0.5;
39const createCollectionDeposit = 100;25const createCollectionDeposit = 100;
40
41let alice: IKeyringPair;
42let bob: IKeyringPair;
4326
44// Skip the inflation block pauses if the block is close to inflation block27// Skip the inflation block pauses if the block is close to inflation block
45// until the inflation happens28// until the inflation happens
62}45}
6346
64describe('integration test: Fees must be credited to Treasury:', () => {47describe('integration test: Fees must be credited to Treasury:', () => {
48 let alice: IKeyringPair;
49 let bob: IKeyringPair;
50
65 before(async () => {51 before(async () => {
66 await usingApi(async (api, privateKeyWrapper) => {52 await usingPlaygrounds(async (helper, privateKey) => {
67 alice = privateKeyWrapper('//Alice');53 const donor = privateKey('//Alice');
68 bob = privateKeyWrapper('//Bob');54 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
69 });55 });
70 });56 });
7157
72 it('Total issuance does not change', async () => {58 itSub('Total issuance does not change', async ({helper}) => {
73 await usingApi(async (api) => {59 const api = helper.api!;
74 await skipInflationBlock(api);60 await skipInflationBlock(api);
75 await waitNewBlocks(api, 1);61 await helper.wait.newBlocks(1);
7662
77 const totalBefore = (await api.query.balances.totalIssuance()).toBigInt();63 const totalBefore = (await api.query.balances.totalIssuance()).toBigInt();
7864
79 const amount = 1n;
80 const transfer = api.tx.balances.transfer(bob.address, amount);65 await helper.balance.transferToSubstrate(alice, bob.address, 1n);
81
82 const result = getGenericResult(await submitTransactionAsync(alice, transfer));
8366
84 const totalAfter = (await api.query.balances.totalIssuance()).toBigInt();67 const totalAfter = (await api.query.balances.totalIssuance()).toBigInt();
8568
86 expect(result.success).to.be.true;
87 expect(totalAfter).to.be.equal(totalBefore);69 expect(totalAfter).to.be.equal(totalBefore);
88 });
89 });70 });
9071
91 it('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async () => {72 itSub('Sender balance decreased by fee+sent amount, Treasury balance increased by fee', async ({helper}) => {
92 await usingApi(async (api) => {
93 await skipInflationBlock(api);73 await skipInflationBlock(helper.api!);
94 await waitNewBlocks(api, 1);74 await helper.wait.newBlocks(1);
9575
96 const treasuryBalanceBefore: bigint = (await api.query.system.account(TREASURY)).data.free.toBigInt();76 const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
97 const aliceBalanceBefore: bigint = (await api.query.system.account(alice.address)).data.free.toBigInt();77 const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
9878
99 const amount = 1n;79 const amount = 1n;
100 const transfer = api.tx.balances.transfer(bob.address, amount);80 await helper.balance.transferToSubstrate(alice, bob.address, amount);
101 const result = getGenericResult(await submitTransactionAsync(alice, transfer));
10281
103 const treasuryBalanceAfter: bigint = (await api.query.system.account(TREASURY)).data.free.toBigInt();82 const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
104 const aliceBalanceAfter: bigint = (await api.query.system.account(alice.address)).data.free.toBigInt();83 const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
84
105 const fee = aliceBalanceBefore - aliceBalanceAfter - amount;85 const fee = aliceBalanceBefore - aliceBalanceAfter - amount;
106 const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;86 const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
10787
108 expect(result.success).to.be.true;
109 expect(treasuryIncrease).to.be.equal(fee);88 expect(treasuryIncrease).to.be.equal(fee);
110 });
111 });89 });
11290
113 it('Treasury balance increased by failed tx fee', async () => {91 itSub('Treasury balance increased by failed tx fee', async ({helper}) => {
114 await usingApi(async (api) => {92 const api = helper.api!;
115 //await skipInflationBlock(api);
116 await waitNewBlocks(api, 1);93 await helper.wait.newBlocks(1);
11794
118 const treasuryBalanceBefore = (await api.query.system.account(TREASURY)).data.free.toBigInt();95 const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
119 const bobBalanceBefore = (await api.query.system.account(bob.address)).data.free.toBigInt();96 const bobBalanceBefore = await helper.balance.getSubstrate(bob.address);
12097
121 const badTx = api.tx.balances.setBalance(alice.address, 0, 0);98 await expect(helper.signTransaction(bob, api.tx.balances.setBalance(alice.address, 0, 0))).to.be.rejected;
122 await expect(submitTransactionExpectFailAsync(bob, badTx)).to.be.rejected;
12399
124 const treasuryBalanceAfter = (await api.query.system.account(TREASURY)).data.free.toBigInt();100 const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
125 const bobBalanceAfter = (await api.query.system.account(bob.address)).data.free.toBigInt();101 const bobBalanceAfter = await helper.balance.getSubstrate(bob.address);
102
126 const fee = bobBalanceBefore - bobBalanceAfter;103 const fee = bobBalanceBefore - bobBalanceAfter;
127 const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;104 const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
128105
129 expect(treasuryIncrease).to.be.equal(fee);106 expect(treasuryIncrease).to.be.equal(fee);
130 });
131 });107 });
132108
133 it('NFT Transactions also send fees to Treasury', async () => {109 itSub('NFT Transactions also send fees to Treasury', async ({helper}) => {
134 await usingApi(async (api) => {
135 await skipInflationBlock(api);110 await skipInflationBlock(helper.api!);
136 await waitNewBlocks(api, 1);111 await helper.wait.newBlocks(1);
137112
138 const treasuryBalanceBefore = (await api.query.system.account(TREASURY)).data.free.toBigInt();113 const treasuryBalanceBefore = await helper.balance.getSubstrate(TREASURY);
139 const aliceBalanceBefore = (await api.query.system.account(alice.address)).data.free.toBigInt();114 const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
140115
141 await createCollectionExpectSuccess();116 await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
142117
143 const treasuryBalanceAfter = (await api.query.system.account(TREASURY)).data.free.toBigInt();118 const treasuryBalanceAfter = await helper.balance.getSubstrate(TREASURY);
144 const aliceBalanceAfter = (await api.query.system.account(alice.address)).data.free.toBigInt();119 const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
145 const fee = aliceBalanceBefore - aliceBalanceAfter;120 const fee = aliceBalanceBefore - aliceBalanceAfter;
146 const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;121 const treasuryIncrease = treasuryBalanceAfter - treasuryBalanceBefore;
147122
148 expect(treasuryIncrease).to.be.equal(fee);123 expect(treasuryIncrease).to.be.equal(fee);
149 });
150 });124 });
151125
152 it('Fees are sane', async () => {126 itSub('Fees are sane', async ({helper}) => {
153 await usingApi(async (api) => {
154 await skipInflationBlock(api);127 const unique = helper.balance.getOneTokenNominal();
155 await waitNewBlocks(api, 1);128 await skipInflationBlock(helper.api!);
129 await helper.wait.newBlocks(1);
156130
157 const aliceBalanceBefore: bigint = (await api.query.system.account(alice.address)).data.free.toBigInt();131 const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
158132
159 await createCollectionExpectSuccess();133 await helper.nft.mintCollection(alice, {name: 'test', description: 'test', tokenPrefix: 'test'});
160134
161 const aliceBalanceAfter: bigint = (await api.query.system.account(alice.address)).data.free.toBigInt();135 const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
162 const fee = aliceBalanceBefore - aliceBalanceAfter;136 const fee = aliceBalanceBefore - aliceBalanceAfter;
163137
164 expect(fee / UNIQUE < BigInt(Math.ceil(saneMaximumFee + createCollectionDeposit))).to.be.true;138 expect(fee / unique < BigInt(Math.ceil(saneMaximumFee + createCollectionDeposit))).to.be.true;
165 expect(fee / UNIQUE < BigInt(Math.ceil(saneMinimumFee + createCollectionDeposit))).to.be.true;139 expect(fee / unique < BigInt(Math.ceil(saneMinimumFee + createCollectionDeposit))).to.be.true;
166 });
167 });140 });
168141
169 it('NFT Transfer fee is close to 0.1 Unique', async () => {142 itSub('NFT Transfer fee is close to 0.1 Unique', async ({helper}) => {
170 await usingApi(async (api) => {
171 await skipInflationBlock(api);143 await skipInflationBlock(helper.api!);
172 await waitNewBlocks(api, 1);144 await helper.wait.newBlocks(1);
173145
174 const collectionId = await createCollectionExpectSuccess();146 const collection = await helper.nft.mintCollection(alice, {
147 name: 'test',
148 description: 'test',
149 tokenPrefix: 'test',
150 });
151 // const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');
175 const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT');152 const token = await collection.mintToken(alice, {Substrate: alice.address});
176153
177 const aliceBalanceBefore: bigint = (await api.query.system.account(alice.address)).data.free.toBigInt();154 const aliceBalanceBefore = await helper.balance.getSubstrate(alice.address);
178 await transferExpectSuccess(collectionId, tokenId, alice, bob, 1, 'NFT');155 await token.transfer(alice, {Substrate: bob.address});
179 const aliceBalanceAfter: bigint = (await api.query.system.account(alice.address)).data.free.toBigInt();156 const aliceBalanceAfter = await helper.balance.getSubstrate(alice.address);
180157
181 const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(UNIQUE);158 const fee = Number(aliceBalanceBefore - aliceBalanceAfter) / Number(helper.balance.getOneTokenNominal());
182 const expectedTransferFee = 0.1;159 const expectedTransferFee = 0.1;
183 // fee drifts because of NextFeeMultiplier160 // fee drifts because of NextFeeMultiplier
184 const tolerance = 0.001;161 const tolerance = 0.001;
185162
186 expect(Math.abs(fee - expectedTransferFee)).to.be.lessThan(tolerance);163 expect(Math.abs(fee - expectedTransferFee)).to.be.lessThan(tolerance);
187 });
188 });164 });
189
190});165});
modifiedtests/src/destroyCollection.test.tsdiffbeforeafterboth
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';
18import chai from 'chai';
19import chaiAsPromised from 'chai-as-promised';
20import {default as usingApi} from './substrate/substrate-api';
21import {createCollectionExpectSuccess,18import {itSub, expect, usingPlaygrounds, Pallets} from './util/playgrounds';
22 destroyCollectionExpectSuccess,
23 destroyCollectionExpectFailure,
24 setCollectionLimitsExpectSuccess,
25 addCollectionAdminExpectSuccess,
26 getCreatedCollectionCount,
27 createItemExpectSuccess,
28 requirePallets,
29 Pallets,
30} from './util/helpers';
31
32chai.use(chaiAsPromised);
3319
34describe('integration test: ext. destroyCollection():', () => {20describe('integration test: ext. destroyCollection():', () => {
21 let alice: IKeyringPair;
22
23 before(async () => {
24 await usingPlaygrounds(async (helper, privateKey) => {
25 const donor = privateKey('//Alice');
26 [alice] = await helper.arrange.createAccounts([100n], donor);
27 });
28 });
29
35 it('NFT collection can be destroyed', async () => {30 itSub('NFT collection can be destroyed', async ({helper}) => {
36 const collectionId = await createCollectionExpectSuccess();31 const collection = await helper.nft.mintCollection(alice, {
32 name: 'test',
33 description: 'test',
34 tokenPrefix: 'test',
35 });
37 await destroyCollectionExpectSuccess(collectionId);36 await collection.burn(alice);
37 expect(await collection.getData()).to.be.null;
38 });38 });
39 it('Fungible collection can be destroyed', async () => {39 itSub('Fungible collection can be destroyed', async ({helper}) => {
40 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});40 const collection = await helper.ft.mintCollection(alice, {
41 name: 'test',
42 description: 'test',
43 tokenPrefix: 'test',
44 }, 0);
41 await destroyCollectionExpectSuccess(collectionId);45 await collection.burn(alice);
46 expect(await collection.getData()).to.be.null;
42 });47 });
43 it('ReFungible collection can be destroyed', async function() {48 itSub.ifWithPallets('ReFungible collection can be destroyed', [Pallets.ReFungible], async ({helper}) => {
44 await requirePallets(this, [Pallets.ReFungible]);
45
46 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});49 const collection = await helper.rft.mintCollection(alice, {
50 name: 'test',
51 description: 'test',
52 tokenPrefix: 'test',
53 });
47 await destroyCollectionExpectSuccess(collectionId);54 await collection.burn(alice);
55 expect(await collection.getData()).to.be.null;
48 });56 });
49});57});
5058
51describe('(!negative test!) integration test: ext. destroyCollection():', () => {59describe('(!negative test!) integration test: ext. destroyCollection():', () => {
52 let alice: IKeyringPair;60 let alice: IKeyringPair;
53 let bob: IKeyringPair;61 let bob: IKeyringPair;
5462
55 before(async () => {63 before(async () => {
56 await usingApi(async (api, privateKeyWrapper) => {64 await usingPlaygrounds(async (helper, privateKey) => {
57 alice = privateKeyWrapper('//Alice');65 const donor = privateKey('//Alice');
58 bob = privateKeyWrapper('//Bob');66 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
59 });67 });
60 });68 });
6169
62 it('(!negative test!) Destroy a collection that never existed', async () => {70 itSub('(!negative test!) Destroy a collection that never existed', async ({helper}) => {
71 const collectionId = 1_000_000;
63 await usingApi(async (api) => {72 await expect(helper.collection.burn(alice, collectionId)).to.be.rejectedWith(/common\.CollectionNotFound/);
64 // Find the collection that never existed
65 const collectionId = await getCreatedCollectionCount(api) + 1;
66 await destroyCollectionExpectFailure(collectionId);
67 });
68 });73 });
69 it('(!negative test!) Destroy a collection that has already been destroyed', async () => {74 itSub('(!negative test!) Destroy a collection that has already been destroyed', async ({helper}) => {
70 const collectionId = await createCollectionExpectSuccess();75 const collection = await helper.nft.mintCollection(alice, {
76 name: 'test',
77 description: 'test',
78 tokenPrefix: 'test',
79 });
71 await destroyCollectionExpectSuccess(collectionId);80 await collection.burn(alice);
72 await destroyCollectionExpectFailure(collectionId);81 await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CollectionNotFound/);
73 });82 });
74 it('(!negative test!) Destroy a collection using non-owner account', async () => {83 itSub('(!negative test!) Destroy a collection using non-owner account', async ({helper}) => {
75 const collectionId = await createCollectionExpectSuccess();84 const collection = await helper.nft.mintCollection(alice, {
85 name: 'test',
86 description: 'test',
87 tokenPrefix: 'test',
88 });
76 await destroyCollectionExpectFailure(collectionId, '//Bob');89 await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/);
77 await destroyCollectionExpectSuccess(collectionId, '//Alice');
78 });90 });
79 it('(!negative test!) Destroy a collection using collection admin account', async () => {91 itSub('(!negative test!) Destroy a collection using collection admin account', async ({helper}) => {
80 const collectionId = await createCollectionExpectSuccess();92 const collection = await helper.nft.mintCollection(alice, {
93 name: 'test',
94 description: 'test',
95 tokenPrefix: 'test',
96 });
81 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);97 await collection.addAdmin(alice, {Substrate: bob.address});
82 await destroyCollectionExpectFailure(collectionId, '//Bob');98 await expect(collection.burn(bob)).to.be.rejectedWith(/common\.NoPermission/);
83 });99 });
84 it('fails when OwnerCanDestroy == false', async () => {100 itSub('fails when OwnerCanDestroy == false', async ({helper}) => {
85 const collectionId = await createCollectionExpectSuccess();101 const collection = await helper.nft.mintCollection(alice, {
86 await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanDestroy: false});102 name: 'test',
87103 description: 'test',
104 tokenPrefix: 'test',
105 limits: {
106 ownerCanDestroy: false,
107 },
108 });
88 await destroyCollectionExpectFailure(collectionId, '//Alice');109 await expect(collection.burn(alice)).to.be.rejectedWith(/common\.NoPermission/);
89 });110 });
90 it('fails when a collection still has a token', async () => {111 itSub('fails when a collection still has a token', async ({helper}) => {
91 const collectionId = await createCollectionExpectSuccess();112 const collection = await helper.nft.mintCollection(alice, {
113 name: 'test',
114 description: 'test',
115 tokenPrefix: 'test',
116 });
92 await createItemExpectSuccess(alice, collectionId, 'NFT');117 await collection.mintToken(alice, {Substrate: alice.address});
93
94 await destroyCollectionExpectFailure(collectionId, '//Alice');118 await expect(collection.burn(alice)).to.be.rejectedWith(/common\.CantDestroyNotEmptyCollection/);
95 });119 });
96});120});
97121
modifiedtests/src/enableDisableTransfer.test.tsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17import chai from 'chai';
18import chaiAsPromised from 'chai-as-promised';17import {IKeyringPair} from '@polkadot/types/types';
19import usingApi from './substrate/substrate-api';
20import {18import {itSub, usingPlaygrounds, expect} from './util/playgrounds';
21 createItemExpectSuccess,
22 createCollectionExpectSuccess,
23 transferExpectSuccess,
24 transferExpectFailure,
25 setTransferFlagExpectSuccess,
26 setTransferFlagExpectFailure,
27} from './util/helpers';
28
29chai.use(chaiAsPromised);
3019
31describe('Enable/Disable Transfers', () => {20describe('Enable/Disable Transfers', () => {
21 let alice: IKeyringPair;
22 let bob: IKeyringPair;
23
32 it('User can transfer token with enabled transfer flag', async () => {24 before(async () => {
33 await usingApi(async (api, privateKeyWrapper) => {25 await usingPlaygrounds(async (helper, privateKey) => {
34 const alice = privateKeyWrapper('//Alice');26 const donor = privateKey('//Alice');
35 const bob = privateKeyWrapper('//Bob');
36 // nft
37 const nftCollectionId = await createCollectionExpectSuccess();
38 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');27 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
39
40 // explicitely set transfer flag
41 await setTransferFlagExpectSuccess(alice, nftCollectionId, true);
42
43 await transferExpectSuccess(nftCollectionId, newNftTokenId, alice, bob, 1);
44 });28 });
45 });29 });
4630
47 it('User can\'n transfer token with disabled transfer flag', async () => {31 itSub('User can transfer token with enabled transfer flag', async ({helper}) => {
48 await usingApi(async (api, privateKeyWrapper) => {32 const collection = await helper.nft.mintCollection(alice, {
33 name: 'test',
34 description: 'test',
35 tokenPrefix: 'test',
36 limits: {
37 transfersEnabled: true,
38 },
49 const alice = privateKeyWrapper('//Alice');39 });
50 const bob = privateKeyWrapper('//Bob');
51 // nft
52 const nftCollectionId = await createCollectionExpectSuccess();40 const token = await collection.mintToken(alice, {Substrate: alice.address});
53 const newNftTokenId = await createItemExpectSuccess(alice, nftCollectionId, 'NFT');41 await token.transfer(alice, {Substrate: bob.address});
54
55 // explicitely set transfer flag
56 await setTransferFlagExpectSuccess(alice, nftCollectionId, false);42 expect(await token.getOwner()).to.be.deep.equal({Substrate: bob.address});
57
58 await transferExpectFailure(nftCollectionId, newNftTokenId, alice, bob, 1);
59 });
60 });43 });
44
45 itSub('User can\'n transfer token with disabled transfer flag', async ({helper}) => {
46 const collection = await helper.nft.mintCollection(alice, {
47 name: 'test',
48 description: 'test',
49 tokenPrefix: 'test',
50 limits: {
51 transfersEnabled: false,
52 },
53 });
54 const token = await collection.mintToken(alice, {Substrate: alice.address});
55 await expect(token.transfer(alice, {Substrate: bob.address})).to.be.rejectedWith(/common\.TransferNotAllowed/);
56 });
61});57});
6258
63describe('Negative Enable/Disable Transfers', () => {59describe('Negative Enable/Disable Transfers', () => {
60 let alice: IKeyringPair;
61 let bob: IKeyringPair;
62
64 it('Non-owner cannot change transfer flag', async () => {63 before(async () => {
65 await usingApi(async (api, privateKeyWrapper) => {64 await usingPlaygrounds(async (helper, privateKey) => {
66 const bob = privateKeyWrapper('//Bob');65 const donor = privateKey('//Alice');
67 // nft
68 const nftCollectionId = await createCollectionExpectSuccess();66 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);
69
70 // Change transfer flag
71 await setTransferFlagExpectFailure(bob, nftCollectionId, false);
72 });67 });
73 });68 });
69
70 itSub('Non-owner cannot change transfer flag', async ({helper}) => {
71 const collection = await helper.nft.mintCollection(alice, {
72 name: 'test',
73 description: 'test',
74 tokenPrefix: 'test',
75 limits: {
76 transfersEnabled: true,
77 },
78 });
79
80 await expect(collection.setLimits(bob, {transfersEnabled: false})).to.be.rejectedWith(/common\.NoPermission/);
81 });
74});82});
7583
modifiedtests/src/nesting/properties.test.tsdiffbeforeafterboth

no syntactic changes

modifiedtests/src/refungible.test.tsdiffbeforeafterboth
124 const token = await collection.mintToken(alice, 100n);124 const token = await collection.mintToken(alice, 100n);
125 expect(await collection.isTokenExists(token.tokenId)).to.be.true;125 expect(await collection.isTokenExists(token.tokenId)).to.be.true;
126 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);126 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
127 expect((await token.burn(alice, 99n)).success).to.be.true;127 expect(await token.burn(alice, 99n)).to.be.true;
128 expect(await collection.isTokenExists(token.tokenId)).to.be.true;128 expect(await collection.isTokenExists(token.tokenId)).to.be.true;
129 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n);129 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(1n);
130 });130 });
136 expect(await collection.isTokenExists(token.tokenId)).to.be.true;136 expect(await collection.isTokenExists(token.tokenId)).to.be.true;
137 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);137 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(100n);
138138
139 expect((await token.burn(alice, 100n)).success).to.be.true;139 expect(await token.burn(alice, 100n)).to.be.true;
140 expect(await collection.isTokenExists(token.tokenId)).to.be.false;140 expect(await collection.isTokenExists(token.tokenId)).to.be.false;
141 });141 });
142142
152 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n);152 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(40n);
153 expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n);153 expect(await token.getBalance({Substrate: bob.address})).to.be.equal(60n);
154154
155 expect((await token.burn(alice, 40n)).success).to.be.true;155 expect(await token.burn(alice, 40n)).to.be.true;
156156
157 expect(await collection.isTokenExists(token.tokenId)).to.be.true;157 expect(await collection.isTokenExists(token.tokenId)).to.be.true;
158 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n);158 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(0n);
159159
160 expect((await token.burn(bob, 59n)).success).to.be.true;160 expect(await token.burn(bob, 59n)).to.be.true;
161161
162 expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n);162 expect(await token.getBalance({Substrate: bob.address})).to.be.equal(1n);
163 expect(await collection.isTokenExists(token.tokenId)).to.be.true;163 expect(await collection.isTokenExists(token.tokenId)).to.be.true;
164164
165 expect((await token.burn(bob, 1n)).success).to.be.true;165 expect(await token.burn(bob, 1n)).to.be.true;
166166
167 expect(await collection.isTokenExists(token.tokenId)).to.be.false;167 expect(await collection.isTokenExists(token.tokenId)).to.be.false;
168 });168 });
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
141 return keyring.addFromUri(seed);141 return keyring.addFromUri(seed);
142 }142 }
143143
144 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult) {144 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {
145 if (creationResult.status !== this.transactionStatus.SUCCESS) {145 if (creationResult.status !== this.transactionStatus.SUCCESS) {
146 throw Error('Unable to create collection!');146 throw Error('Unable to create collection!');
147 }147 }
160 return collectionId;160 return collectionId;
161 }161 }
162162
163 static extractTokensFromCreationResult(creationResult: ITransactionResult) {163 static extractTokensFromCreationResult(creationResult: ITransactionResult): {
164 success: boolean,
165 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],
166 } {
164 if (creationResult.status !== this.transactionStatus.SUCCESS) {167 if (creationResult.status !== this.transactionStatus.SUCCESS) {
165 throw Error('Unable to create tokens!');168 throw Error('Unable to create tokens!');
166 }169 }
167 let success = false;170 let success = false;
168 const tokens = [] as any;171 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];
169 creationResult.result.events.forEach(({event: {data, method, section}}) => {172 creationResult.result.events.forEach(({event: {data, method, section}}) => {
170 if (method === 'ExtrinsicSuccess') {173 if (method === 'ExtrinsicSuccess') {
171 success = true;174 success = true;
172 } else if ((section === 'common') && (method === 'ItemCreated')) {175 } else if ((section === 'common') && (method === 'ItemCreated')) {
173 tokens.push({176 tokens.push({
174 collectionId: parseInt(data[0].toString(), 10),177 collectionId: parseInt(data[0].toString(), 10),
175 tokenId: parseInt(data[1].toString(), 10),178 tokenId: parseInt(data[1].toString(), 10),
176 owner: data[2].toJSON(),179 owner: data[2].toHuman(),
180 amount: data[3].toBigInt(),
177 });181 });
178 }182 }
179 });183 });
180 return {success, tokens};184 return {success, tokens};
181 }185 }
182186
183 static extractTokensFromBurnResult(burnResult: ITransactionResult) {187 static extractTokensFromBurnResult(burnResult: ITransactionResult): {
188 success: boolean,
189 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],
190 } {
184 if (burnResult.status !== this.transactionStatus.SUCCESS) {191 if (burnResult.status !== this.transactionStatus.SUCCESS) {
185 throw Error('Unable to burn tokens!');192 throw Error('Unable to burn tokens!');
186 }193 }
187 let success = false;194 let success = false;
188 const tokens = [] as any;195 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];
189 burnResult.result.events.forEach(({event: {data, method, section}}) => {196 burnResult.result.events.forEach(({event: {data, method, section}}) => {
190 if (method === 'ExtrinsicSuccess') {197 if (method === 'ExtrinsicSuccess') {
191 success = true;198 success = true;
192 } else if ((section === 'common') && (method === 'ItemDestroyed')) {199 } else if ((section === 'common') && (method === 'ItemDestroyed')) {
193 tokens.push({200 tokens.push({
194 collectionId: parseInt(data[0].toString(), 10),201 collectionId: parseInt(data[0].toString(), 10),
195 tokenId: parseInt(data[1].toString(), 10),202 tokenId: parseInt(data[1].toString(), 10),
196 owner: data[2].toJSON(),203 owner: data[2].toHuman(),
204 amount: data[3].toBigInt(),
197 });205 });
198 }206 }
199 });207 });
200 return {success, tokens};208 return {success, tokens};
201 }209 }
202210
203 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string) {211 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {
204 let eventId = null;212 let eventId = null;
205 events.forEach(({event: {data, method, section}}) => {213 events.forEach(({event: {data, method, section}}) => {
206 if ((section === expectedSection) && (method === expectedMethod)) {214 if ((section === expectedSection) && (method === expectedMethod)) {
1013 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1021 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);
1014 }1022 }
10151023
1016 /**1024 /**
1017 *1025 *
1018 * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.1026 * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.
1019 *1027 *
1020 * @param signer keyring of signer1028 * @param signer keyring of signer
1021 * @param collectionId ID of collection1029 * @param collectionId ID of collection
1022 * @param tokenId ID of token1030 * @param tokenId ID of token
1023 * @param amount amount of tokens to be burned. For NFT must be set to 1n1031 * @param amount amount of tokens to be burned. For NFT must be set to 1n
1024 * @example burnToken(aliceKeyring, 10, 5);1032 * @example burnToken(aliceKeyring, 10, 5);
1025 * @returns ```true``` and burnt token number is extrinsic success. Otherwise ```false``` and ```null```1033 * @returns ```true``` if the extrinsic is successful, otherwise ```false```
1026 */1034 */
1027 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<{1035 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {
1028 success: boolean,
1029 token: number | null
1030 }> {
1031 const burnResult = await this.helper.executeExtrinsic(1036 const burnResult = await this.helper.executeExtrinsic(
1032 signer,1037 signer,
1033 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1038 'api.tx.unique.burnItem', [collectionId, tokenId, amount],
1034 true, // `Unable to burn token for ${label}`,1039 true, // `Unable to burn token for ${label}`,
1035 );1040 );
1036 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1041 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);
1037 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1042 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');
1038 return {success: burnedTokens.success, token: burnedTokens.tokens.length > 0 ? burnedTokens.tokens[0] : null};1043 return burnedTokens.success;
1039 }1044 }
10401045
1041 /**1046 /**
1161 if (tokenData === null || tokenData.owner === null) return null;1166 if (tokenData === null || tokenData.owner === null) return null;
1162 const owner = {} as any;1167 const owner = {} as any;
1163 for (const key of Object.keys(tokenData.owner)) {1168 for (const key of Object.keys(tokenData.owner)) {
1164 owner[key.toLocaleLowerCase()] = new CrossAccountId(tokenData.owner[key]).withNormalizedSubstrate();1169 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'
1170 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])
1171 : tokenData.owner[key];
1165 }1172 }
1166 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1173 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);
1167 return tokenData;1174 return tokenData;
1704 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1711 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));
1705 }1712 }
17061713
1707 /**1714 /**
1708 * Destroys a concrete instance of RFT.1715 * Destroys a concrete instance of RFT.
1709 * @param signer keyring of signer1716 * @param signer keyring of signer
1710 * @param collectionId ID of collection1717 * @param collectionId ID of collection
1711 * @param tokenId ID of token1718 * @param tokenId ID of token
1712 * @param amount number of pieces to be burnt1719 * @param amount number of pieces to be burnt
1713 * @example burnToken(aliceKeyring, 10, 5);1720 * @example burnToken(aliceKeyring, 10, 5);
1714 * @returns ```true``` and burnt token number, if extrinsic is successful. Otherwise ```false``` and ```null```1721 * @returns ```true``` if the extrinsic is successful, otherwise ```false```
1715 */1722 */
1716 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<{ success: boolean; token: number | null; }> {1723 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {
1717 return await super.burnToken(signer, collectionId, tokenId, amount);1724 return await super.burnToken(signer, collectionId, tokenId, amount);
1718 }1725 }
17191726
1919 * @returns ```true``` if extrinsic success, otherwise ```false```1926 * @returns ```true``` if extrinsic success, otherwise ```false```
1920 */1927 */
1921 async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {1928 async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {
1922 return (await super.burnToken(signer, collectionId, 0, amount)).success;1929 return await super.burnToken(signer, collectionId, 0, amount);
1923 }1930 }
19241931
1925 /**1932 /**