1234567891011121314151617import {ApiPromise} from '@polkadot/api';18import {IKeyringPair} from '@polkadot/types/types';19import chai from 'chai';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';40import {usingPlaygrounds} from './util/playgrounds';4142chai.use(chaiAsPromised);43const expect = chai.expect;4445let donor: IKeyringPair;4647before(async () => {48 await usingPlaygrounds(async (_, privateKey) => {49 donor = privateKey('//Alice');50 });51});5253let alice: IKeyringPair;54let bob: IKeyringPair;5556describe('Integration Test createMultipleItems(collection_id, owner, items_data):', () => {57 before(async () => {58 await usingPlaygrounds(async (helper) => {59 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);60 });61 });6263 it('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async () => {64 await usingPlaygrounds(async (helper) => {65 const collection = await helper.nft.mintCollection(alice, {66 name: 'name',67 description: 'descr',68 tokenPrefix: 'COL',69 tokenPropertyPermissions: [70 {key: 'data', permission: {tokenOwner: true, mutable: false, collectionAdmin: false}},71 ],72 });73 const args = [74 {properties: [{key: 'data', value: '1'}]},75 {properties: [{key: 'data', value: '2'}]},76 {properties: [{key: 'data', value: '3'}]},77 ];78 const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);79 for (const [i, token] of tokens.entries()) {80 const tokenData = await token.getData();81 expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: alice.address});82 expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);83 }84 });85 });8687 it('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async () => {88 await usingPlaygrounds(async (helper) => {89 const collection = await helper.ft.mintCollection(alice, {90 name: 'name',91 description: 'descr',92 tokenPrefix: 'COL',93 });94 const args = [95 {value: 1n},96 {value: 2n},97 {value: 3n},98 ];99 await helper.ft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);100 expect(await collection.getBalance({Substrate: alice.address})).to.be.equal(6n);101 });102 });103104 it('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', async function() {105 await usingPlaygrounds(async (helper) => {106 const collection = await helper.rft.mintCollection(alice, {107 name: 'name',108 description: 'descr',109 tokenPrefix: 'COL',110 });111 const args = [112 {pieces: 1n},113 {pieces: 2n},114 {pieces: 3n},115 ];116 const tokens = await helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);117118 for (const [i, token] of tokens.entries()) {119 expect(await token.getBalance({Substrate: alice.address})).to.be.equal(BigInt(i + 1));120 }121 });122 });123124 it('Can mint amount of items equals to collection limits', async () => {125 await usingPlaygrounds(async (helper) => {126 const collection = await helper.nft.mintCollection(alice, {127 name: 'name',128 description: 'descr',129 tokenPrefix: 'COL',130 limits: {131 tokenLimit: 2,132 },133 });134 const args = [{}, {}];135 await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);136 });137 });138139 it('Create 0x31, 0x32, 0x33 items in active NFT with property Admin', async () => {140 await usingPlaygrounds(async (helper) => {141 const collection = await helper.nft.mintCollection(alice, {142 name: 'name',143 description: 'descr',144 tokenPrefix: 'COL',145 tokenPropertyPermissions: [146 {key: 'data', permission: {tokenOwner: false, mutable: true, collectionAdmin: true}},147 ],148 });149 const args = [150 {properties: [{key: 'data', value: '1'}]},151 {properties: [{key: 'data', value: '2'}]},152 {properties: [{key: 'data', value: '3'}]},153 ];154 const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);155 for (const [i, token] of tokens.entries()) {156 const tokenData = await token.getData();157 expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: alice.address});158 expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);159 }160 });161 });162163 it('Create 0x31, 0x32, 0x33 items in active NFT with property AdminConst', async () => {164 await usingPlaygrounds(async (helper) => {165 const collection = await helper.nft.mintCollection(alice, {166 name: 'name',167 description: 'descr',168 tokenPrefix: 'COL',169 tokenPropertyPermissions: [170 {key: 'data', permission: {tokenOwner: false, mutable: false, collectionAdmin: true}},171 ],172 });173 const args = [174 {properties: [{key: 'data', value: '1'}]},175 {properties: [{key: 'data', value: '2'}]},176 {properties: [{key: 'data', value: '3'}]},177 ];178 const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);179 for (const [i, token] of tokens.entries()) {180 const tokenData = await token.getData();181 expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: alice.address});182 expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);183 }184 });185 });186187 it('Create 0x31, 0x32, 0x33 items in active NFT with property itemOwnerOrAdmin', async () => {188 await usingPlaygrounds(async (helper) => {189 const collection = await helper.nft.mintCollection(alice, {190 name: 'name',191 description: 'descr',192 tokenPrefix: 'COL',193 tokenPropertyPermissions: [194 {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},195 ],196 });197 const args = [198 {properties: [{key: 'data', value: '1'}]},199 {properties: [{key: 'data', value: '2'}]},200 {properties: [{key: 'data', value: '3'}]},201 ];202 const tokens = await helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);203 for (const [i, token] of tokens.entries()) {204 const tokenData = await token.getData();205 expect(tokenData?.normalizedOwner).to.be.deep.equal({Substrate: alice.address});206 expect(tokenData?.properties[0].value).to.be.equal(args[i].properties[0].value);207 }208 });209 });210});211212describe('Integration Test createMultipleItems(collection_id, owner, items_data) with collection admin permissions:', () => {213 let alice: IKeyringPair;214 let bob: IKeyringPair;215216 before(async () => {217 await usingApi(async (api, privateKeyWrapper) => {218 alice = privateKeyWrapper('//Alice');219 bob = privateKeyWrapper('//Bob');220 });221 });222223 it('Create 0x31, 0x32, 0x33 items in active NFT collection and verify tokens data in chain', async () => {224 await usingApi(async (api: ApiPromise) => {225 const collectionId = await createCollectionWithPropsExpectSuccess({propPerm: [{key: 'data', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}]});226 const itemsListIndexBefore = await getLastTokenId(api, collectionId);227 expect(itemsListIndexBefore).to.be.equal(0);228 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);229 const args = [230 {NFT: {properties: [{key: 'data', value: 'v1'}]}},231 {NFT: {properties: [{key: 'data', value: 'v2'}]}},232 {NFT: {properties: [{key: 'data', value: 'v3'}]}},233 ];234 const createMultipleItemsTx = api.tx.unique235 .createMultipleItems(collectionId, normalizeAccountId(bob.address), args);236 await submitTransactionAsync(bob, createMultipleItemsTx);237 const itemsListIndexAfter = await getLastTokenId(api, collectionId);238 expect(itemsListIndexAfter).to.be.equal(3);239240 expect(await getTokenOwner(api, collectionId, 1)).to.be.deep.equal(normalizeAccountId(bob.address));241 expect(await getTokenOwner(api, collectionId, 2)).to.be.deep.equal(normalizeAccountId(bob.address));242 expect(await getTokenOwner(api, collectionId, 3)).to.be.deep.equal(normalizeAccountId(bob.address));243244 expect((await getTokenProperties(api, collectionId, 1, ['data']))[0].value).to.be.equal('v1');245 expect((await getTokenProperties(api, collectionId, 2, ['data']))[0].value).to.be.equal('v2');246 expect((await getTokenProperties(api, collectionId, 3, ['data']))[0].value).to.be.equal('v3');247 });248 });249250 it('Create 0x01, 0x02, 0x03 items in active Fungible collection and verify tokens data in chain', async () => {251 await usingApi(async (api: ApiPromise) => {252 const collectionId = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}});253 const itemsListIndexBefore = await getLastTokenId(api, collectionId);254 expect(itemsListIndexBefore).to.be.equal(0);255 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);256 const args = [257 {Fungible: {value: 1}},258 {Fungible: {value: 2}},259 {Fungible: {value: 3}},260 ];261 const createMultipleItemsTx = api.tx.unique262 .createMultipleItems(collectionId, normalizeAccountId(bob.address), args);263 await submitTransactionAsync(bob, createMultipleItemsTx);264 const token1Data = await getBalance(api, collectionId, bob.address, 0);265266 expect(token1Data).to.be.equal(6n); 267 });268 });269270 it('Create 0x31, 0x32, 0x33 items in active ReFungible collection and verify tokens data in chain', async function() {271 await requirePallets(this, [Pallets.ReFungible]);272273 await usingApi(async (api: ApiPromise) => {274 const collectionId = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}});275 const itemsListIndexBefore = await getLastTokenId(api, collectionId);276 expect(itemsListIndexBefore).to.be.equal(0);277 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);278 const args = [279 {ReFungible: {pieces: 1}},280 {ReFungible: {pieces: 2}},281 {ReFungible: {pieces: 3}},282 ];283 const createMultipleItemsTx = api.tx.unique284 .createMultipleItems(collectionId, normalizeAccountId(bob.address), args);285 await submitTransactionAsync(bob, createMultipleItemsTx);286 const itemsListIndexAfter = await getLastTokenId(api, collectionId);287 expect(itemsListIndexAfter).to.be.equal(3);288289 expect(await getBalance(api, collectionId, bob.address, 1)).to.be.equal(1n);290 expect(await getBalance(api, collectionId, bob.address, 2)).to.be.equal(2n);291 expect(await getBalance(api, collectionId, bob.address, 3)).to.be.equal(3n);292 });293 });294});295296describe('Negative Integration Test createMultipleItems(collection_id, owner, items_data):', () => {297 let alice: IKeyringPair;298 let bob: IKeyringPair;299300 before(async () => {301 await usingPlaygrounds(async (helper) => {302 [alice, bob] = await helper.arrange.createAccounts([100n, 100n], donor);303 });304 });305306 it('Regular user cannot create items in active NFT collection', async () => {307 await usingPlaygrounds(async (helper) => {308 const collection = await helper.nft.mintCollection(alice, {309 name: 'name',310 description: 'descr',311 tokenPrefix: 'COL',312 });313 const args = [314 {},315 {},316 ];317 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);318 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);319 });320 });321322 it('Regular user cannot create items in active Fungible collection', async () => {323 await usingPlaygrounds(async (helper) => {324 const collection = await helper.ft.mintCollection(alice, {325 name: 'name',326 description: 'descr',327 tokenPrefix: 'COL',328 });329 const args = [330 {value: 1n},331 {value: 2n},332 {value: 3n},333 ];334 const mintTx = async () => helper.ft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);335 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);336 });337 });338339 it('Regular user cannot create items in active ReFungible collection', async function() {340 await usingPlaygrounds(async (helper) => {341 const collection = await helper.rft.mintCollection(alice, {342 name: 'name',343 description: 'descr',344 tokenPrefix: 'COL',345 });346 const args = [347 {pieces: 1n},348 {pieces: 1n},349 {pieces: 1n},350 ];351 const mintTx = async () => helper.rft.mintMultipleTokensWithOneOwner(bob, collection.collectionId, {Substrate: alice.address}, args);352 await expect(mintTx()).to.be.rejectedWith(/common\.PublicMintingNotAllowed/);353 });354 });355356 it('Create token in not existing collection', async () => {357 await usingPlaygrounds(async (helper) => {358 const collectionId = 1_000_000;359 const args = [360 {},361 {},362 ];363 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(bob, collectionId, {Substrate: alice.address}, args);364 await expect(mintTx()).to.be.rejectedWith(/common\.CollectionNotFound/);365 });366 });367368 it('Create NFTs that has reached the maximum data limit', async function() {369 await usingPlaygrounds(async (helper) => {370 const collection = await helper.nft.mintCollection(alice, {371 name: 'name',372 description: 'descr',373 tokenPrefix: 'COL',374 tokenPropertyPermissions: [375 {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},376 ],377 });378 const args = [379 {properties: [{key: 'data', value: 'A'.repeat(32769)}]},380 {properties: [{key: 'data', value: 'B'.repeat(32769)}]},381 {properties: [{key: 'data', value: 'C'.repeat(32769)}]},382 ];383 const mintTx = async () => helper.nft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);384 await expect(mintTx()).to.be.rejected;385 });386 });387388 it('Create Refungible tokens that has reached the maximum data limit', async function() {389 await usingPlaygrounds(async (helper) => {390 const collection = await helper.rft.mintCollection(alice, {391 name: 'name',392 description: 'descr',393 tokenPrefix: 'COL',394 tokenPropertyPermissions: [395 {key: 'data', permission: {tokenOwner: true, mutable: true, collectionAdmin: true}},396 ],397 });398 const args = [399 {pieces: 10n, properties: [{key: 'data', value: 'A'.repeat(32769)}]},400 {pieces: 10n, properties: [{key: 'data', value: 'B'.repeat(32769)}]},401 {pieces: 10n, properties: [{key: 'data', value: 'C'.repeat(32769)}]},402 ];403 const mintTx = async () => helper.rft.mintMultipleTokensWithOneOwner(alice, collection.collectionId, {Substrate: alice.address}, args);404 await expect(mintTx()).to.be.rejected;405 });406 });407408 it('Create tokens with different types', async () => {409 await usingPlaygrounds(async (helper) => {410 const {collectionId} = await helper.nft.mintCollection(alice, {411 name: 'name',412 description: 'descr',413 tokenPrefix: 'COL',414 });415416 417 const types = ['NFT', 'Fungible', 'ReFungible'];418 const mintTx = helper.api?.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), types);419 await expect(helper.signTransaction(alice, mintTx)).to.be.rejected;420 });421 });422423 it('Create tokens with different data limits <> maximum data limit', async () => {424 await usingApi(async (api: ApiPromise) => {425 const collectionId = await createCollectionWithPropsExpectSuccess({426 propPerm: [{key: 'key', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}],427 });428 const args = [429 {NFT: {properties: [{key: 'key', value: 'A'}]}},430 {NFT: {properties: [{key: 'key', value: 'B'.repeat(32769)}]}},431 ];432 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);433 await expect(submitTransactionExpectFailAsync(alice, createMultipleItemsTx)).to.be.rejected;434 });435 });436437 it('Fails when minting tokens exceeds collectionLimits amount', async () => {438 await usingApi(async (api) => {439 const collectionId = await createCollectionExpectSuccess();440 await setCollectionLimitsExpectSuccess(alice, collectionId, {441 tokenLimit: 1,442 });443 const args = [444 {NFT: {}},445 {NFT: {}},446 ];447 const createMultipleItemsTx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);448 await expect(executeTransaction(api, alice, createMultipleItemsTx)).to.be.rejectedWith(/common\.CollectionTokenLimitExceeded/);449 });450 });451452 it('User doesnt have editing rights', async () => {453 await usingApi(async (api: ApiPromise) => {454 const collectionId = await createCollectionWithPropsExpectSuccess({455 propPerm: [{key: 'key1', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}],456 });457 const itemsListIndexBefore = await getLastTokenId(api, collectionId);458 expect(itemsListIndexBefore).to.be.equal(0);459 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);460 const args = [461 {NFT: {properties: [{key: 'key1', value: 'v2'}]}},462 {NFT: {}},463 {NFT: {}},464 ];465466 const tx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(bob.address), args);467 await expect(executeTransaction(api, bob, tx)).to.be.rejectedWith(/common\.NoPermission/);468 });469 });470471 it('Adding property without access rights', async () => {472 await usingApi(async (api: ApiPromise) => {473 const collectionId = await createCollectionWithPropsExpectSuccess({properties: [{key: 'k', value: 'v1'}]});474 const itemsListIndexBefore = await getLastTokenId(api, collectionId);475 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);476 expect(itemsListIndexBefore).to.be.equal(0);477 const args = [{NFT: {properties: [{key: 'k', value: 'v'}]}},478 {NFT: {}},479 {NFT: {}}];480481 const tx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(bob.address), args);482 await expect(executeTransaction(api, bob, tx)).to.be.rejectedWith(/common\.NoPermission/);483 });484 });485486 it('Adding more than 64 prps', async () => {487 await usingApi(async (api: ApiPromise) => {488 const propPerms = [{key: 'key', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}];489 for (let i = 0; i < 65; i++) {490 propPerms.push({key: `key${i}`, permission: {mutable: true, collectionAdmin: true, tokenOwner: true}});491 }492493 const collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});494495 const tx1 = api.tx.unique.setTokenPropertyPermissions(collectionId, propPerms);496 await expect(executeTransaction(api, alice, tx1)).to.be.rejectedWith(/common\.PropertyLimitReached/);497498 const itemsListIndexBefore = await getLastTokenId(api, collectionId);499 expect(itemsListIndexBefore).to.be.equal(0);500 await addCollectionAdminExpectSuccess(alice, collectionId, bob.address);501502 const prps = [];503504 for (let i = 0; i < 65; i++) {505 prps.push({key: `key${i}`, value: `value${i}`});506 }507508 const args = [509 {NFT: {properties: prps}},510 {NFT: {properties: prps}},511 {NFT: {properties: prps}},512 ];513514 515 const tx2 = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);516 await expect(submitTransactionExpectFailAsync(alice, tx2)).to.be.rejected;517 });518 });519520 it('Trying to add bigger property than allowed', async () => {521 await usingApi(async (api: ApiPromise) => {522 const collectionId = await createCollectionWithPropsExpectSuccess({523 propPerm: [{key: 'k', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}],524 });525 const itemsListIndexBefore = await getLastTokenId(api, collectionId);526 expect(itemsListIndexBefore).to.be.equal(0);527 const args = [{NFT: {properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]}},528 {NFT: {properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]}},529 {NFT: {properties: [{key: 'k', value: 'vvvvvv'.repeat(5000)}, {key: 'k2', value: 'vvv'.repeat(5000)}]}}];530531 const tx = api.tx.unique.createMultipleItems(collectionId, normalizeAccountId(alice.address), args);532 await expect(executeTransaction(api, alice, tx)).to.be.rejectedWith(/common\.NoPermission/);533 });534 });535});