difftreelog
fix tests cleanup
in: master
10 files changed
tests/src/eth/collectionLimits.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionLimits.test.ts
+++ b/tests/src/eth/collectionLimits.test.ts
@@ -97,7 +97,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'ISNI', testCase.case, 18)).send();
- const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);
// Cannot set non-existing limit
await expect(collectionEvm.methods
tests/src/eth/createCollection.test.tsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.8//9// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {evmToAddress} from '@polkadot/util-crypto';19import {Pallets, requirePalletsOrSkip} from '../util';20import {expect, itEth, usingEthPlaygrounds} from './util';21import {CREATE_COLLECTION_DATA_DEFAULTS, CollectionLimitField, CollectionMode, CreateCollectionData, TokenPermissionField, emptyAddress} from './util/playgrounds/types';22import {CollectionFlag, IEthCrossAccountId, TCollectionMode} from '../util/playgrounds/types';2324const DECIMALS = 18;25const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [26 [],27 [],28 [],29 [false, false, []],30 [],31 [0],32];3334type ElementOf<A> = A extends readonly (infer T)[] ? T : never;35function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {36 if(args.length === 0) {37 yield internalRest as any;38 return;39 }40 for(const value of args[0]) {41 yield* cartesian([...internalRest, value], ...args.slice(1)) as any;42 }43}4445describe('Create collection from EVM', () => {46 let donor: IKeyringPair;47 let nominal: bigint;4849 before(async function() {50 await usingEthPlaygrounds(async (helper, privateKey) => {51 donor = await privateKey({url: import.meta.url});52 nominal = helper.balance.getOneTokenNominal();53 });54 });5556 describe('Fungible collection', () => {57 before(async function() {58 await usingEthPlaygrounds((helper) => {59 requirePalletsOrSkip(this, helper, [Pallets.Fungible]);60 return Promise.resolve();61 });62 });6364 itEth('Collection address exist', async ({helper}) => {65 const owner = await helper.eth.createAccountWithBalance(donor);66 const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';67 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);6869 expect(await collectionHelpers70 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())71 .to.be.false;7273 const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();74 expect(await collectionHelpers75 .methods.isCollectionExist(collectionAddress).call())76 .to.be.true;7778 // check collectionOwner:79 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);80 const collectionOwner = await collectionEvm.methods.collectionOwner().call();81 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));82 });8384 itEth('destroyCollection', async ({helper}) => {85 const owner = await helper.eth.createAccountWithBalance(donor);86 const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();87 const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);8889 const result = await collectionHelper.methods90 .destroyCollection(collectionAddress)91 .send({from: owner});9293 const events = helper.eth.normalizeEvents(result.events);9495 expect(events).to.be.deep.equal([96 {97 address: collectionHelper.options.address,98 event: 'CollectionDestroyed',99 args: {100 collectionId: collectionAddress,101 },102 },103 ]);104105 expect(await collectionHelper.methods106 .isCollectionExist(collectionAddress)107 .call()).to.be.false;108 expect(await helper.collection.getData(collectionId)).to.be.null;109 });110111 itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {112 const owner = await helper.eth.createAccountWithBalance(donor);113 const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);114 {115 const MAX_NAME_LENGTH = 64;116 const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);117 const description = 'A';118 const tokenPrefix = 'A';119120 await expect(collectionHelper.methods121 .createCollection([122 emptyAddress,123 collectionName,124 description,125 tokenPrefix,126 CollectionMode.Fungible,127 DECIMALS,128 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,129 ])130 .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);131 }132 {133 const MAX_DESCRIPTION_LENGTH = 256;134 const collectionName = 'A';135 const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);136 const tokenPrefix = 'A';137 await expect(collectionHelper.methods138 .createCollection([139 emptyAddress,140 collectionName,141 description,142 tokenPrefix,143 CollectionMode.Fungible,144 DECIMALS,145 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,146 ])147 .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);148 }149 {150 const MAX_TOKEN_PREFIX_LENGTH = 16;151 const collectionName = 'A';152 const description = 'A';153 const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);154 await expect(collectionHelper.methods155 .createCollection([156 emptyAddress,157 collectionName,158 description,159 tokenPrefix,160 CollectionMode.Fungible,161 DECIMALS,162 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,163 ])164 .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);165 }166 });167168 itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {169 const owner = await helper.eth.createAccountWithBalance(donor);170 const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);171 const expects = [0n, 1n, 30n].map(async value => {172 await expect(collectionHelper.methods173 .createCollection([174 emptyAddress,175 'Peasantry',176 'absolutely anything',177 'TWIW',178 CollectionMode.Fungible,179 DECIMALS,180 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,181 ])182 .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');183 });184 await Promise.all(expects);185 });186 });187188 describe('Nonfungible collection', () => {189 before(async function() {190 await usingEthPlaygrounds((helper) => {191 requirePalletsOrSkip(this, helper, [Pallets.NFT]);192 return Promise.resolve();193 });194 });195196 itEth('Create collection', async ({helper}) => {197 const owner = await helper.eth.createAccountWithBalance(donor);198199 const name = 'CollectionEVM';200 const description = 'Some description';201 const prefix = 'token prefix';202203 // todo:playgrounds this might fail when in async environment.204 const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;205 const {collectionId, collectionAddress, events} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'nft')).send();206207 expect(events).to.be.deep.equal([208 {209 address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',210 event: 'CollectionCreated',211 args: {212 owner: owner,213 collectionId: collectionAddress,214 },215 },216 ]);217218 const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;219220 const collection = helper.nft.getCollectionObject(collectionId);221 const data = (await collection.getData())!;222223 expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);224 expect(collectionId).to.be.eq(collectionCountAfter);225 expect(data.name).to.be.eq(name);226 expect(data.description).to.be.eq(description);227 expect(data.raw.tokenPrefix).to.be.eq(prefix);228 expect(data.raw.mode).to.be.eq('NFT');229230 const options = await collection.getOptions();231232 expect(options.tokenPropertyPermissions).to.be.empty;233 });234235 // this test will occasionally fail when in async environment.236 itEth('Check collection address exist', async ({helper}) => {237 const owner = await helper.eth.createAccountWithBalance(donor);238239 const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;240 const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);241 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);242243 expect(await collectionHelpers.methods244 .isCollectionExist(expectedCollectionAddress)245 .call()).to.be.false;246247 await collectionHelpers.methods248 .createCollection([249 emptyAddress,250 'A',251 'A',252 'A',253 CollectionMode.Nonfungible,254 0,255 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,256 ])257 .send({value: Number(2n * helper.balance.getOneTokenNominal())});258259 expect(await collectionHelpers.methods260 .isCollectionExist(expectedCollectionAddress)261 .call()).to.be.true;262 });263 });264265 describe('Create RFT collection from EVM', () => {266 before(async function() {267 await usingEthPlaygrounds((helper) => {268 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);269 return Promise.resolve();270 });271 });272273 itEth('Create collection', async ({helper}) => {274 const owner = await helper.eth.createAccountWithBalance(donor);275276 const name = 'CollectionEVM';277 const description = 'Some description';278 const prefix = 'token prefix';279280 const {collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'rft')).send();281 const data = (await helper.rft.getData(collectionId))!;282 const collection = helper.rft.getCollectionObject(collectionId);283284 expect(data.name).to.be.eq(name);285 expect(data.description).to.be.eq(description);286 expect(data.raw.tokenPrefix).to.be.eq(prefix);287 expect(data.raw.mode).to.be.eq('ReFungible');288289 const options = await collection.getOptions();290291 expect(options.tokenPropertyPermissions).to.be.empty;292 });293294 itEth('Create collection with properties & get description', async ({helper}) => {295 const owner = await helper.eth.createAccountWithBalance(donor);296297 const name = 'CollectionEVM';298 const description = 'Some description';299 const prefix = 'token prefix';300 const baseUri = 'BaseURI';301302 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);303 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');304305 const collection = helper.rft.getCollectionObject(collectionId);306 const data = (await collection.getData())!;307308 expect(data.name).to.be.eq(name);309 expect(data.description).to.be.eq(description);310 expect(data.raw.tokenPrefix).to.be.eq(prefix);311 expect(data.raw.mode).to.be.eq('ReFungible');312313 expect(await contract.methods.description().call()).to.deep.equal(description);314315 const options = await collection.getOptions();316 expect(options.tokenPropertyPermissions).to.be.deep.equal([317 {318 key: 'URI',319 permission: {mutable: true, collectionAdmin: true, tokenOwner: false},320 },321 {322 key: 'URISuffix',323 permission: {mutable: true, collectionAdmin: true, tokenOwner: false},324 },325 ]);326 });327328 itEth('Set sponsorship', async ({helper}) => {329 const owner = await helper.eth.createAccountWithBalance(donor);330 const sponsor = await helper.eth.createAccountWithBalance(donor);331 const ss58Format = helper.chain.getChainProperties().ss58Format;332 const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Sponsor', 'absolutely anything', 'ENVY', 'rft')).send();333334 const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);335 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);336 await collection.methods.setCollectionSponsorCross(sponsorCross).send();337338 let data = (await helper.rft.getData(collectionId))!;339 expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));340341 await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');342343 const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);344 await sponsorCollection.methods.confirmCollectionSponsorship().send();345346 data = (await helper.rft.getData(collectionId))!;347 expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));348 });349350 itEth('Collection address exist', async ({helper}) => {351 const owner = await helper.eth.createAccountWithBalance(donor);352 const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';353 const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);354355 expect(await collectionHelpers356 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())357 .to.be.false;358359 const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'rft')).send();360 expect(await collectionHelpers361 .methods.isCollectionExist(collectionAddress).call())362 .to.be.true;363364 // check collectionOwner:365 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);366 const collectionOwner = await collectionEvm.methods.collectionOwner().call();367 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));368 });369370 itEth('destroyCollection', async ({helper}) => {371 const owner = await helper.eth.createAccountWithBalance(donor);372 const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'OLF', 'rft')).send();373 const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);374375 await expect(collectionHelper.methods376 .destroyCollection(collectionAddress)377 .send({from: owner})).to.be.fulfilled;378379 expect(await collectionHelper.methods380 .isCollectionExist(collectionAddress)381 .call()).to.be.false;382 expect(await helper.collection.getData(collectionId)).to.be.null;383 });384385 itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {386 const owner = await helper.eth.createAccountWithBalance(donor);387 const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);388 {389 const MAX_NAME_LENGTH = 64;390 const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);391 const description = 'A';392 const tokenPrefix = 'A';393394 await expect(collectionHelper.methods395 .createCollection([396 emptyAddress,397 collectionName,398 description,399 tokenPrefix,400 CollectionMode.Refungible,401 DECIMALS,402 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,403 ])404 .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);405 }406 {407 const MAX_DESCRIPTION_LENGTH = 256;408 const collectionName = 'A';409 const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);410 const tokenPrefix = 'A';411 await expect(collectionHelper.methods412 .createCollection([413 emptyAddress,414 collectionName,415 description,416 tokenPrefix,417 CollectionMode.Refungible,418 DECIMALS,419 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,420 ])421 .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);422 }423 {424 const MAX_TOKEN_PREFIX_LENGTH = 16;425 const collectionName = 'A';426 const description = 'A';427 const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);428 await expect(collectionHelper.methods429 .createCollection([430 emptyAddress,431 collectionName,432 description,433 tokenPrefix,434 CollectionMode.Refungible,435 DECIMALS,436 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,437 ])438 .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);439 }440 });441442 itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {443 const owner = await helper.eth.createAccountWithBalance(donor);444 const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);445 await expect(collectionHelper.methods446 .createCollection([447 emptyAddress,448 'Peasantry',449 'absolutely anything',450 'TWIW',451 CollectionMode.Refungible,452 0,453 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,454 ])455 .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');456 });457 });458459 describe('Sponsoring', () => {460 itEth('Сan remove collection sponsor', async ({helper}) => {461 const owner = await helper.eth.createAccountWithBalance(donor);462 const sponsor = await helper.eth.createAccountWithBalance(donor);463 const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);464465 const {collectionAddress} = await helper.eth.createCollection(466 owner,467 {468 ...CREATE_COLLECTION_DATA_DEFAULTS,469 name: 'Sponsor collection',470 description: '1',471 tokenPrefix: '1',472 collectionMode: 'nft',473 pendingSponsor: sponsorCross,474 },475 ).send();476 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);477478 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;479480 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});481 let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});482 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));483 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;484485 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});486487 sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});488 expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');489 });490491 itEth('Can sponsor from evm address via access list', async ({helper}) => {492 const owner = await helper.eth.createAccountWithBalance(donor);493 const sponsorEth = await helper.eth.createAccountWithBalance(donor);494 const sponsorCross = helper.ethCrossAccount.fromAddr(sponsorEth);495496 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(497 owner,498 {499 ...CREATE_COLLECTION_DATA_DEFAULTS,500 name: 'Sponsor collection',501 description: '1',502 tokenPrefix: '1',503 collectionMode: 'nft',504 pendingSponsor: sponsorCross,505 limits: [{field: CollectionLimitField.SponsoredDataRateLimit, value: 30n}],506 tokenPropertyPermissions: [{key: 'key', permissions: [{code: TokenPermissionField.TokenOwner, value: true}]}],507 },508 '',509 );510511 const collectionSub = helper.nft.getCollectionObject(collectionId);512 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);513514 let sponsorship = (await collectionSub.getData())!.raw.sponsorship;515 expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));516 // Account cannot confirm sponsorship if it is not set as a sponsor517 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');518519 // Sponsor can confirm sponsorship:520 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});521 sponsorship = (await collectionSub.getData())!.raw.sponsorship;522 expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));523524 // Create user with no balance:525 const user = helper.ethCrossAccount.createAccount();526 const nextTokenId = await collectionEvm.methods.nextTokenId().call();527 expect(nextTokenId).to.be.equal('1');528529 // Set collection permissions:530 const oldPermissions = (await collectionSub.getData())!.raw.permissions;531 expect(oldPermissions.mintMode).to.be.false;532 expect(oldPermissions.access).to.be.equal('Normal');533534 await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});535 await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});536 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});537538 const newPermissions = (await collectionSub.getData())!.raw.permissions;539 expect(newPermissions.mintMode).to.be.true;540 expect(newPermissions.access).to.be.equal('AllowList');541542 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));543 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));544 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));545546 // User can mint token without balance:547 {548 const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});549 const event = helper.eth.normalizeEvents(result.events)550 .find(event => event.event === 'Transfer');551552 expect(event).to.be.deep.equal({553 address: collectionAddress,554 event: 'Transfer',555 args: {556 from: '0x0000000000000000000000000000000000000000',557 to: user.eth,558 tokenId: '1',559 },560 });561562 // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth});563564 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));565 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));566 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));567568 expect(await collectionEvm.methods.properties(nextTokenId, []).call())569 .to.be.like([570 [571 'key',572 '0x' + Buffer.from('Value').toString('hex'),573 ],574 ]);575 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);576 expect(userBalanceAfter).to.be.eq(userBalanceBefore);577 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;578 }579 });580581 itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => {582 const owner = await helper.eth.createAccountWithBalance(donor);583 const sponsor = await helper.eth.createAccountWithBalance(donor);584 const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);585 const user = helper.eth.createAccount();586 const userCross = helper.ethCrossAccount.fromAddress(user);587588 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(589 owner,590 {591 ...CREATE_COLLECTION_DATA_DEFAULTS,592 name: 'Sponsor collection',593 description: '1',594 tokenPrefix: '1',595 collectionMode: 'nft',596 pendingSponsor: sponsorCross,597 adminList: [userCross],598 },599 '',600 );601602 const collectionSub = helper.nft.getCollectionObject(collectionId);603 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);604 // Set collection sponsor:605 let collectionData = (await collectionSub.getData())!;606 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));607 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');608609 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});610 collectionData = (await collectionSub.getData())!;611 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));612613 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));614 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));615616 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});617 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;618619 const event = helper.eth.normalizeEvents(mintingResult.events)620 .find(event => event.event === 'Transfer');621 const address = helper.ethAddress.fromCollectionId(collectionId);622623 expect(event).to.be.deep.equal({624 address,625 event: 'Transfer',626 args: {627 from: '0x0000000000000000000000000000000000000000',628 to: user,629 tokenId: '1',630 },631 });632 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');633634 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));635 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);636 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));637 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;638 });639640 itEth('Can reassign collection sponsor', async ({helper}) => {641 const owner = await helper.eth.createAccountWithBalance(donor);642 const sponsorEth = await helper.eth.createAccountWithBalance(donor);643 const sponsorCrossEth = helper.ethCrossAccount.fromAddr(sponsorEth);644 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);645 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);646647 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(648 owner,649 {650 ...CREATE_COLLECTION_DATA_DEFAULTS,651 name: 'Sponsor collection',652 description: '1',653 tokenPrefix: '1',654 collectionMode: 'nft',655 pendingSponsor: sponsorCrossEth,656 },657 '',658 );659 const collectionSub = helper.nft.getCollectionObject(collectionId);660 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);661662 // Set and confirm sponsor:663 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});664665 // Can reassign sponsor:666 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});667 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;668 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});669 });670671 [672 'transfer',673 'transferCross',674 'transferFrom',675 'transferFromCross',676 ].map(testCase =>677 itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {678 const owner = await helper.eth.createAccountWithBalance(donor);679 const sponsor = await helper.eth.createAccountWithBalance(donor);680 const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);681 const user = await helper.eth.createAccountWithBalance(donor);682 const userCross = helper.ethCrossAccount.fromAddress(user);683684 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(685 owner,686 {687 ...CREATE_COLLECTION_DATA_DEFAULTS,688 name: 'Sponsor collection',689 description: '1',690 tokenPrefix: '1',691 collectionMode: 'rft',692 pendingSponsor: sponsorCross,693 adminList: [userCross],694 },695 '',696 );697 const receiver = await helper.eth.createAccountWithBalance(donor);698 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);699700 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});701702 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});703 const tokenId = result.events.Transfer.returnValues.tokenId;704705 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));706 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));707 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));708709 switch (testCase) {710 case 'transfer':711 await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});712 break;713 case 'transferCross':714 await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});715 break;716 case 'transferFrom':717 await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});718 break;719 case 'transferFromCross':720 await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});721 break;722 }723724 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));725 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);726 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));727 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;728 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));729 expect(userBalanceAfter).to.be.eq(userBalanceBefore);730 }));731 });732733 describe('Collection admins', () => {734 let donor: IKeyringPair;735736 before(async function() {737 await usingEthPlaygrounds(async (_helper, privateKey) => {738 donor = await privateKey({url: import.meta.url});739 });740 });741742 [743 {mode: 'nft' as const, requiredPallets: []},744 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},745 {mode: 'ft' as const, requiredPallets: []},746 ].map(testCase => {747 itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {748 // arrange749 const owner = await helper.eth.createAccountWithBalance(donor);750 const adminSub = await privateKey('//admin2');751 const adminEth = helper.eth.createAccount().toLowerCase();752753 const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);754 const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);755756 const {collectionAddress, collectionId} = await helper.eth.createCollection(757 owner,758 {759 ...CREATE_COLLECTION_DATA_DEFAULTS,760 name: 'Mint collection',761 description: 'a',762 tokenPrefix: 'b',763 collectionMode: testCase.mode,764 adminList: [adminCrossSub, adminCrossEth],765 },766 ).send();767 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);768769 // 1. Expect api.rpc.unique.adminlist returns admins:770 const adminListRpc = await helper.collection.getAdmins(collectionId);771 expect(adminListRpc).to.has.length(2);772 expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}]);773774 // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist775 let adminListEth = await collectionEvm.methods.collectionAdmins().call();776 adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));777 expect(adminListRpc).to.be.like(adminListEth);778779 // 3. check isOwnerOrAdminCross returns true:780 expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;781 expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;782 });783 });784785 itEth('cross account admin can mint', async ({helper}) => {786 // arrange: create collection and accounts787 const owner = await helper.eth.createAccountWithBalance(donor);788 const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();789 const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);790 const [adminSub] = await helper.arrange.createAccounts([100n], donor);791 const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);792 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(793 owner,794 {795 ...CREATE_COLLECTION_DATA_DEFAULTS,796 name: 'Mint collection',797 description: 'a',798 tokenPrefix: 'b',799 collectionMode: 'nft',800 adminList: [adminCrossSub, adminCrossEth],801 },802 'uri',803 );804 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);805806 // admin (sub and eth) can mint token:807 await collectionEvm.methods.mint(owner).send({from: adminEth});808 await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}});809810 expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2);811 });812813 itEth('cannot add invalid cross account admin', async ({helper}) => {814 const owner = await helper.eth.createAccountWithBalance(donor);815 const [admin] = await helper.arrange.createAccounts([100n, 100n], donor);816817 const adminCross = {818 eth: helper.address.substrateToEth(admin.address),819 sub: admin.addressRaw,820 };821822 await expect(helper.eth.createCollection(823 owner,824 {825 ...CREATE_COLLECTION_DATA_DEFAULTS,826 name: 'A',827 description: 'B',828 tokenPrefix: 'C',829 collectionMode: 'nft',830 adminList: [adminCross],831 },832 ).call()).to.be.rejected;833 });834835 itEth('Remove [cross] admin by owner', async ({helper}) => {836 const owner = await helper.eth.createAccountWithBalance(donor);837838 const [adminSub] = await helper.arrange.createAccounts([10n], donor);839 const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();840 const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);841 const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);842843 const {collectionAddress, collectionId} = await helper.eth.createCollection(844 owner,845 {846 ...CREATE_COLLECTION_DATA_DEFAULTS,847 name: 'A',848 description: 'B',849 tokenPrefix: 'C',850 collectionMode: 'nft',851 adminList: [adminCrossSub, adminCrossEth],852 },853 ).send();854855 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);856857 {858 const adminList = await helper.collection.getAdmins(collectionId);859 expect(adminList).to.deep.include({Substrate: adminSub.address});860 expect(adminList).to.deep.include({Ethereum: adminEth});861 }862863 await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send();864 await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send();865 const adminList = await helper.collection.getAdmins(collectionId);866 expect(adminList.length).to.be.eq(0);867868 // Non admin cannot mint:869 await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);870 await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected;871 });872 });873874 describe('Collection limits', () => {875 describe('Can set collection limits', () => {876 [877 {case: 'nft' as const},878 {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},879 {case: 'ft' as const},880 ].map(testCase =>881 itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {882 const owner = await helper.eth.createAccountWithBalance(donor);883884 const limits = {885 accountTokenOwnershipLimit: 1000n,886 sponsoredDataSize: 1024n,887 sponsoredDataRateLimit: 30n,888 tokenLimit: 1000000n,889 sponsorTransferTimeout: 6n,890 sponsorApproveTimeout: 6n,891 ownerCanTransfer: 1n,892 ownerCanDestroy: 0n,893 transfersEnabled: 0n,894 };895896 const {collectionId, collectionAddress} = await helper.eth.createCollection(897 owner,898 {899 ...CREATE_COLLECTION_DATA_DEFAULTS,900 name: 'Limits',901 description: 'absolutely anything',902 tokenPrefix: 'FLO',903 collectionMode: testCase.case,904 limits: [905 {field: CollectionLimitField.AccountTokenOwnership, value: limits.accountTokenOwnershipLimit},906 {field: CollectionLimitField.SponsoredDataSize, value: limits.sponsoredDataSize},907 {field: CollectionLimitField.SponsoredDataRateLimit, value: limits.sponsoredDataRateLimit},908 {field: CollectionLimitField.TokenLimit, value: limits.tokenLimit},909 {field: CollectionLimitField.SponsorTransferTimeout, value: limits.sponsorTransferTimeout},910 {field: CollectionLimitField.SponsorApproveTimeout, value: limits.sponsorApproveTimeout},911 {field: CollectionLimitField.OwnerCanTransfer, value: limits.ownerCanTransfer},912 {field: CollectionLimitField.OwnerCanDestroy, value: limits.ownerCanDestroy},913 {field: CollectionLimitField.TransferEnabled, value: limits.transfersEnabled},914 ],915 },916 ).send();917918 const expectedLimits = {919 accountTokenOwnershipLimit: 1000,920 sponsoredDataSize: 1024,921 sponsoredDataRateLimit: {blocks: 30},922 tokenLimit: 1000000,923 sponsorTransferTimeout: 6,924 sponsorApproveTimeout: 6,925 ownerCanTransfer: true,926 ownerCanDestroy: false,927 transfersEnabled: false,928 };929930 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);931932 // Check limits from sub:933 const data = (await helper.rft.getData(collectionId))!;934 expect(data.raw.limits).to.deep.eq(expectedLimits);935 expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits);936 // Check limits from eth:937 const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner});938 expect(limitsEvm).to.have.length(9);939 expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]);940 expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]);941 expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]);942 expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]);943 expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]);944 expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]);945 expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]);946 expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]);947 expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]);948 }));949 });950951 describe('(!negative test!) Cannot set invalid collection limits', () => {952 [953 {case: 'nft' as const},954 {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},955 {case: 'ft' as const},956 ].map(testCase =>957 itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {958 const invalidLimits = {959 accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER),960 transfersEnabled: 3,961 };962963 const createCollectionData = {964 ...CREATE_COLLECTION_DATA_DEFAULTS,965 name: 'Limits',966 description: 'absolutely anything',967 tokenPrefix: 'ISNI',968 collectionMode: testCase.case,969 };970971 const owner = await helper.eth.createAccountWithBalance(donor);972 await expect(helper.eth.createCollection(973 owner,974 {975 ...createCollectionData,976 limits: [{field: 9 as CollectionLimitField, value: 1n}],977 },978 ).call()).to.be.rejectedWith('value not convertible into enum "CollectionLimitField"');979980 await expect(helper.eth.createCollection(981 owner,982 {983 ...createCollectionData,984 limits: [{field: CollectionLimitField.AccountTokenOwnership, value: invalidLimits.accountTokenOwnershipLimit}],985 },986 ).call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`);987988 await expect(helper.eth.createCollection(989 owner,990 {991 ...createCollectionData,992 limits: [{field: CollectionLimitField.TransferEnabled, value: 3n}],993 },994 ).call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`);995996 await expect(helper.eth.createCollection(997 owner,998 {999 ...createCollectionData,1000 limits: [{field: CollectionLimitField.SponsoredDataSize, value: -1n}],1001 },1002 ).call()).to.be.rejectedWith('value out-of-bounds');1003 }));1004 });1005 });10061007 describe('Collection properties', () => {10081009 [1010 {mode: 'nft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1011 {mode: 'rft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1012 {mode: 'ft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1013 ].map(testCase =>1014 itEth.ifWithPallets(`Collection properties can be set for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {1015 const caller = await helper.eth.createAccountWithBalance(donor);1016 const callerCross = helper.ethCrossAccount.fromAddress(caller);1017 const owner = await helper.eth.createAccountWithBalance(donor);1018 const {collectionId, collectionAddress} = await helper.eth.createCollection(1019 owner,1020 {1021 ...CREATE_COLLECTION_DATA_DEFAULTS,1022 name: 'name',1023 description: 'test',1024 tokenPrefix: 'test',1025 collectionMode: testCase.mode,1026 adminList: [callerCross],1027 properties: testCase.methodParams,1028 },1029 ).send();10301031 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);10321033 const raw = (await helper[testCase.mode].getData(collectionId))?.raw;1034 expect(raw.properties).to.deep.equal(testCase.expectedProps);10351036 // collectionProperties returns properties:1037 expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));1038 }));10391040 [1041 {mode: 'nft' as const},1042 {mode: 'rft' as const},1043 {mode: 'ft' as const},1044 ].map(testCase =>1045 itEth.ifWithPallets(`Collection properties can be deleted for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {1046 const caller = await helper.eth.createAccountWithBalance(donor);1047 const callerCross = helper.ethCrossAccount.fromAddress(caller);1048 const owner = await helper.eth.createAccountWithBalance(donor);1049 const {collectionId, collectionAddress} = await helper.eth.createCollection(1050 owner,1051 {1052 ...CREATE_COLLECTION_DATA_DEFAULTS,1053 name: 'name',1054 description: 'test',1055 tokenPrefix: 'test',1056 collectionMode: testCase.mode,1057 adminList: [callerCross],1058 properties:[1059 {key: 'testKey1', value: Buffer.from('testValue1')},1060 {key: 'testKey2', value: Buffer.from('testValue2')},1061 {key: 'testKey3', value: Buffer.from('testValue3')}],1062 },1063 ).send();10641065 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);10661067 await collectionEvm.methods.deleteCollectionProperties(['testKey1', 'testKey2']).send({from: caller});10681069 const raw = (await helper[testCase.mode].getData(collectionId))?.raw;10701071 expect(raw.properties.length).to.equal(1);1072 expect(raw.properties).to.deep.equal([{key: 'testKey3', value: 'testValue3'}]);1073 }));10741075 itEth('(!negative test!) Cannot set invalid properties', async({helper}) => {1076 const caller = await helper.eth.createAccountWithBalance(donor);1077 const callerCross = helper.ethCrossAccount.fromAddress(caller);1078 const owner = await helper.eth.createAccountWithBalance(donor);1079 const createCollectionData = {1080 ...CREATE_COLLECTION_DATA_DEFAULTS,1081 name: 'name',1082 description: 'test',1083 tokenPrefix: 'test',1084 collectionMode: 'nft' as TCollectionMode,1085 adminList: [callerCross],1086 };1087 await expect(helper.eth.createCollection(1088 owner,1089 {1090 ...createCollectionData,1091 properties: [{key: '', value: Buffer.from('val1')}],1092 },1093 ).call()).to.be.rejected;10941095 await expect(helper.eth.createCollection(1096 owner,1097 {1098 ...createCollectionData,1099 properties: [{key: 'déjà vu', value: Buffer.from('hmm...')}],1100 },1101 ).call()).to.be.rejected;11021103 await expect(helper.eth.createCollection(1104 owner,1105 {1106 ...createCollectionData,1107 properties: [{key: 'a'.repeat(257), value: Buffer.from('val3')}],1108 },1109 ).call()).to.be.rejected;1110 });11111112 itEth('(!negative test!) cannot delete properties of non-owned collections', async ({helper}) => {1113 const caller = await helper.eth.createAccountWithBalance(donor);1114 const owner = await helper.eth.createAccountWithBalance(donor);1115 const {collectionAddress} = await helper.eth.createCollection(1116 owner,1117 {1118 ...CREATE_COLLECTION_DATA_DEFAULTS,1119 name: 'name',1120 description: 'test',1121 tokenPrefix: 'test',1122 collectionMode: 'nft',1123 properties:[1124 {key: 'testKey1', value: Buffer.from('testValue1')},1125 {key: 'testKey2', value: Buffer.from('testValue2')}],1126 },1127 ).send();11281129 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);11301131 await expect(collectionEvm.methods.deleteCollectionProperties(['testKey2']).send({from: caller})).to.be.rejected;1132 });1133 });11341135 describe('Token property permissions', () => {1136 [1137 {mode: 'nft' as const, requiredPallets: []},1138 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1139 ].map(testCase =>1140 itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {1141 const owner = await helper.eth.createAccountWithBalance(donor);1142 const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);1143 for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {1144 const {collectionId, collectionAddress} = await helper.eth.createCollection(1145 owner,1146 {1147 ...CREATE_COLLECTION_DATA_DEFAULTS,1148 name: 'A',1149 description: 'B',1150 tokenPrefix: 'C',1151 collectionMode: testCase.mode,1152 adminList: [caller],1153 tokenPropertyPermissions: [1154 {1155 key: 'testKey',1156 permissions: [1157 {code: TokenPermissionField.Mutable, value: mutable},1158 {code: TokenPermissionField.TokenOwner, value: tokenOwner},1159 {code: TokenPermissionField.CollectionAdmin, value: collectionAdmin},1160 ],1161 },1162 ],1163 },1164 ).send();1165 const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);11661167 expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{1168 key: 'testKey',1169 permission: {mutable, collectionAdmin, tokenOwner},1170 }]);11711172 expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([1173 ['testKey', [1174 [TokenPermissionField.Mutable.toString(), mutable],1175 [TokenPermissionField.TokenOwner.toString(), tokenOwner],1176 [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]],1177 ],1178 ]);1179 }1180 }));11811182 [1183 {mode: 'nft' as const, requiredPallets: []},1184 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1185 ].map(testCase =>1186 itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions`, testCase.requiredPallets, async({helper}) => {1187 const owner = await helper.eth.createAccountWithBalance(donor);1188 const {collectionId, collectionAddress} = await helper.eth.createCollection(1189 owner,1190 {1191 ...CREATE_COLLECTION_DATA_DEFAULTS,1192 name: 'A',1193 description: 'B',1194 tokenPrefix: 'C',1195 collectionMode: testCase.mode,1196 tokenPropertyPermissions: [1197 {1198 key: 'testKey_0',1199 permissions: [1200 {code: TokenPermissionField.Mutable, value: true},1201 {code: TokenPermissionField.TokenOwner, value: true},1202 {code: TokenPermissionField.CollectionAdmin, value: true}],1203 },1204 {1205 key: 'testKey_1',1206 permissions: [1207 {code: TokenPermissionField.Mutable, value: true},1208 {code: TokenPermissionField.TokenOwner, value: false},1209 {code: TokenPermissionField.CollectionAdmin, value: true}],1210 },1211 {1212 key: 'testKey_2',1213 permissions: [1214 {code: TokenPermissionField.Mutable, value: false},1215 {code: TokenPermissionField.TokenOwner, value: true},1216 {code: TokenPermissionField.CollectionAdmin, value: false}],1217 },1218 ],1219 },1220 ).send();1221 const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);12221223 expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([1224 {1225 key: 'testKey_0',1226 permission: {mutable: true, tokenOwner: true, collectionAdmin: true},1227 },1228 {1229 key: 'testKey_1',1230 permission: {mutable: true, tokenOwner: false, collectionAdmin: true},1231 },1232 {1233 key: 'testKey_2',1234 permission: {mutable: false, tokenOwner: true, collectionAdmin: false},1235 },1236 ]);12371238 expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([1239 ['testKey_0', [1240 [TokenPermissionField.Mutable.toString(), true],1241 [TokenPermissionField.TokenOwner.toString(), true],1242 [TokenPermissionField.CollectionAdmin.toString(), true]],1243 ],1244 ['testKey_1', [1245 [TokenPermissionField.Mutable.toString(), true],1246 [TokenPermissionField.TokenOwner.toString(), false],1247 [TokenPermissionField.CollectionAdmin.toString(), true]],1248 ],1249 ['testKey_2', [1250 [TokenPermissionField.Mutable.toString(), false],1251 [TokenPermissionField.TokenOwner.toString(), true],1252 [TokenPermissionField.CollectionAdmin.toString(), false]],1253 ],1254 ]);1255 }));12561257 [1258 {mode: 'nft' as const, requiredPallets: []},1259 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1260 ].map(testCase =>1261 itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {1262 const caller = await helper.eth.createAccountWithBalance(donor);1263 const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);1264 const {collectionAddress} = await helper.eth.createCollection(1265 caller,1266 {1267 ...CREATE_COLLECTION_DATA_DEFAULTS,1268 name: 'A',1269 description: 'B',1270 tokenPrefix: 'C',1271 collectionMode: testCase.mode,1272 adminList: [receiver],1273 tokenPropertyPermissions: [1274 {1275 key: 'testKey',1276 permissions: [1277 {code: TokenPermissionField.Mutable, value: true},1278 {code: TokenPermissionField.CollectionAdmin, value: true}],1279 },1280 {1281 key: 'testKey_1',1282 permissions: [1283 {code: TokenPermissionField.Mutable, value: true},1284 {code: TokenPermissionField.CollectionAdmin, value: true}],1285 },1286 ],1287 },1288 ).send();12891290 const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);1291 const tokenId = (await collection.methods.mintCross(receiver, [{key: 'testKey', value: Buffer.from('testValue')}, {key: 'testKey_1', value: Buffer.from('testValue_1')}]).send()).events.Transfer.returnValues.tokenId;1292 expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(2);12931294 await collection.methods.deleteProperties(tokenId, ['testKey', 'testKey_1']).send({from: caller});1295 expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(0);1296 }));1297 });12981299 describe('Nesting', () => {1300 itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {1301 const owner = await helper.eth.createAccountWithBalance(donor);1302 const {collectionAddress, collectionId} = await helper.eth.createCollection(1303 owner,1304 {1305 ...CREATE_COLLECTION_DATA_DEFAULTS,1306 name: 'A',1307 description: 'B',1308 tokenPrefix: 'C',1309 collectionMode: 'nft',1310 nestingSettings: {token_owner: true, collection_admin: false, restricted: []},1311 },1312 ).send();13131314 const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13151316 // Create a token to be nested to1317 const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});1318 const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;1319 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);13201321 // Create a nested token1322 const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});1323 const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;1324 expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);13251326 // Create a token to be nested and nest1327 const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});1328 const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;13291330 await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});1331 expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);13321333 // Unnest token back1334 await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});1335 expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);1336 });13371338 itEth('NFT: collectionNesting()', async ({helper}) => {1339 const owner = await helper.eth.createAccountWithBalance(donor);1340 const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createCollection(1341 owner,1342 new CreateCollectionData('A', 'B', 'C', 'nft'),1343 ).send();13441345 const unnestedContract = await helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);1346 expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);13471348 const {collectionAddress} = await helper.eth.createCollection(1349 owner,1350 {1351 ...CREATE_COLLECTION_DATA_DEFAULTS,1352 name: 'A',1353 description: 'B',1354 tokenPrefix: 'C',1355 collectionMode: 'nft',1356 nestingSettings: {token_owner: true, collection_admin: false, restricted: [unnestedCollectionAddress.toString()]},1357 },1358 ).send();13591360 const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);1361 expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress.toString()]]);1362 await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});1363 expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);1364 });13651366 itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {1367 const owner = await helper.eth.createAccountWithBalance(donor);13681369 const {collectionId, collectionAddress} = await helper.eth.createCollection(1370 owner,1371 {1372 ...CREATE_COLLECTION_DATA_DEFAULTS,1373 name: 'A',1374 description: 'B',1375 tokenPrefix: 'C',1376 collectionMode: 'nft',1377 nestingSettings: {token_owner: false, collection_admin: false, restricted: []},1378 },1379 ).send();13801381 const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13821383 // Create a token to nest into1384 const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});1385 const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;1386 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);13871388 // Create a token to nest1389 const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});1390 const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;13911392 // Try to nest1393 await expect(contract.methods1394 .transfer(targetNftTokenAddress, nftTokenId)1395 .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');1396 });1397 });13981399 describe('Flags', () => {1400 const createCollectionData = {1401 ...CREATE_COLLECTION_DATA_DEFAULTS,1402 name: 'A',1403 description: 'B',1404 tokenPrefix: 'C',1405 collectionMode: 'nft' as TCollectionMode,1406 };14071408 itEth('NFT: use numbers for flags', async ({helper}) => {1409 const owner = await helper.eth.createAccountWithBalance(donor);14101411 {1412 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 0}).send();1413 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});1414 }14151416 {1417 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 64}).send();1418 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1419 }1420 });14211422 itEth('NFT: foreign flag number is ignored', async ({helper}) => {1423 const owner = await helper.eth.createAccountWithBalance(donor);14241425 {1426 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).send();1427 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});1428 }14291430 {1431 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).send();1432 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1433 }1434 });14351436 itEth('NFT: use enum for flags', async ({helper}) => {1437 const owner = await helper.eth.createAccountWithBalance(donor);14381439 {1440 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata]}).send();1441 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1442 }1443 });14441445 itEth('NFT: foreign flag enum is ignored', async ({helper}) => {1446 const owner = await helper.eth.createAccountWithBalance(donor);14471448 {1449 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).send();1450 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});1451 }14521453 {1454 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).send();1455 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1456 }1457 });1458 });1459});1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.8//9// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {IKeyringPair} from '@polkadot/types/types';18import {evmToAddress} from '@polkadot/util-crypto';19import {Pallets, requirePalletsOrSkip} from '../util';20import {expect, itEth, usingEthPlaygrounds} from './util';21import {CREATE_COLLECTION_DATA_DEFAULTS, CollectionLimitField, CollectionMode, CreateCollectionData, TokenPermissionField, emptyAddress} from './util/playgrounds/types';22import {CollectionFlag, IEthCrossAccountId, TCollectionMode} from '../util/playgrounds/types';2324const DECIMALS = 18;25const CREATE_COLLECTION_DATA_DEFAULTS_ARRAY = [26 [], // properties27 [], // tokenPropertyPermissions28 [], // adminList29 [false, false, []], // nestingSettings30 [], // limits31 emptyAddress, // pendingSponsor32 [0], // flags33];3435type ElementOf<A> = A extends readonly (infer T)[] ? T : never;36function* cartesian<T extends Array<Array<any>>, R extends Array<any>>(internalRest: [...R], ...args: [...T]): Generator<[...R, ...{[K in keyof T]: ElementOf<T[K]>}]> {37 if(args.length === 0) {38 yield internalRest as any;39 return;40 }41 for(const value of args[0]) {42 yield* cartesian([...internalRest, value], ...args.slice(1)) as any;43 }44}4546describe('Create collection from EVM', () => {47 let donor: IKeyringPair;48 let nominal: bigint;4950 before(async function() {51 await usingEthPlaygrounds(async (helper, privateKey) => {52 donor = await privateKey({url: import.meta.url});53 nominal = helper.balance.getOneTokenNominal();54 });55 });5657 describe('Fungible collection', () => {58 before(async function() {59 await usingEthPlaygrounds((helper) => {60 requirePalletsOrSkip(this, helper, [Pallets.Fungible]);61 return Promise.resolve();62 });63 });6465 itEth('Collection address exist', async ({helper}) => {66 const owner = await helper.eth.createAccountWithBalance(donor);67 const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';68 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);6970 expect(await collectionHelpers71 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())72 .to.be.false;7374 const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();75 expect(await collectionHelpers76 .methods.isCollectionExist(collectionAddress).call())77 .to.be.true;7879 // check collectionOwner:80 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);81 const collectionOwner = await collectionEvm.methods.collectionOwner().call();82 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));83 });8485 itEth('destroyCollection', async ({helper}) => {86 const owner = await helper.eth.createAccountWithBalance(donor);87 const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'ft', DECIMALS)).send();88 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);8990 const result = await collectionHelper.methods91 .destroyCollection(collectionAddress)92 .send({from: owner});9394 const events = helper.eth.normalizeEvents(result.events);9596 expect(events).to.be.deep.equal([97 {98 address: collectionHelper.options.address,99 event: 'CollectionDestroyed',100 args: {101 collectionId: collectionAddress,102 },103 },104 ]);105106 expect(await collectionHelper.methods107 .isCollectionExist(collectionAddress)108 .call()).to.be.false;109 expect(await helper.collection.getData(collectionId)).to.be.null;110 });111112 itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {113 const owner = await helper.eth.createAccountWithBalance(donor);114 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);115 {116 const MAX_NAME_LENGTH = 64;117 const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);118 const description = 'A';119 const tokenPrefix = 'A';120121 await expect(collectionHelper.methods122 .createCollection([123 collectionName,124 description,125 tokenPrefix,126 CollectionMode.Fungible,127 DECIMALS,128 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,129 ])130 .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);131 }132 {133 const MAX_DESCRIPTION_LENGTH = 256;134 const collectionName = 'A';135 const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);136 const tokenPrefix = 'A';137 await expect(collectionHelper.methods138 .createCollection([139 collectionName,140 description,141 tokenPrefix,142 CollectionMode.Fungible,143 DECIMALS,144 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,145 ])146 .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);147 }148 {149 const MAX_TOKEN_PREFIX_LENGTH = 16;150 const collectionName = 'A';151 const description = 'A';152 const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);153 await expect(collectionHelper.methods154 .createCollection([155 collectionName,156 description,157 tokenPrefix,158 CollectionMode.Fungible,159 DECIMALS,160 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,161 ])162 .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);163 }164 });165166 itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {167 const owner = await helper.eth.createAccountWithBalance(donor);168 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);169 const expects = [0n, 1n, 30n].map(async value => {170 await expect(collectionHelper.methods171 .createCollection([172 'Peasantry',173 'absolutely anything',174 'TWIW',175 CollectionMode.Fungible,176 DECIMALS,177 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,178 ])179 .call({value: Number(value * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');180 });181 await Promise.all(expects);182 });183 });184185 describe('Nonfungible collection', () => {186 before(async function() {187 await usingEthPlaygrounds((helper) => {188 requirePalletsOrSkip(this, helper, [Pallets.NFT]);189 return Promise.resolve();190 });191 });192193 itEth('Create collection', async ({helper}) => {194 const owner = await helper.eth.createAccountWithBalance(donor);195196 const name = 'CollectionEVM';197 const description = 'Some description';198 const prefix = 'token prefix';199200 // todo:playgrounds this might fail when in async environment.201 const collectionCountBefore = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;202 const {collectionId, collectionAddress, events} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'nft')).send();203204 expect(events).to.be.deep.equal([205 {206 address: '0x6C4E9fE1AE37a41E93CEE429e8E1881aBdcbb54F',207 event: 'CollectionCreated',208 args: {209 owner: owner,210 collectionId: collectionAddress,211 },212 },213 ]);214215 const collectionCountAfter = +(await helper.callRpc('api.rpc.unique.collectionStats')).created;216217 const collection = helper.nft.getCollectionObject(collectionId);218 const data = (await collection.getData())!;219220 expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);221 expect(collectionId).to.be.eq(collectionCountAfter);222 expect(data.name).to.be.eq(name);223 expect(data.description).to.be.eq(description);224 expect(data.raw.tokenPrefix).to.be.eq(prefix);225 expect(data.raw.mode).to.be.eq('NFT');226227 const options = await collection.getOptions();228229 expect(options.tokenPropertyPermissions).to.be.empty;230 });231232 // this test will occasionally fail when in async environment.233 itEth('Check collection address exist', async ({helper}) => {234 const owner = await helper.eth.createAccountWithBalance(donor);235236 const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;237 const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);238 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);239240 expect(await collectionHelpers.methods241 .isCollectionExist(expectedCollectionAddress)242 .call()).to.be.false;243244 await collectionHelpers.methods245 .createCollection([246 'A',247 'A',248 'A',249 CollectionMode.Nonfungible,250 0,251 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,252 ])253 .send({value: Number(2n * helper.balance.getOneTokenNominal())});254255 expect(await collectionHelpers.methods256 .isCollectionExist(expectedCollectionAddress)257 .call()).to.be.true;258 });259 });260261 describe('Create RFT collection from EVM', () => {262 before(async function() {263 await usingEthPlaygrounds((helper) => {264 requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);265 return Promise.resolve();266 });267 });268269 itEth('Create collection', async ({helper}) => {270 const owner = await helper.eth.createAccountWithBalance(donor);271272 const name = 'CollectionEVM';273 const description = 'Some description';274 const prefix = 'token prefix';275276 const {collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData(name, description, prefix, 'rft')).send();277 const data = (await helper.rft.getData(collectionId))!;278 const collection = helper.rft.getCollectionObject(collectionId);279280 expect(data.name).to.be.eq(name);281 expect(data.description).to.be.eq(description);282 expect(data.raw.tokenPrefix).to.be.eq(prefix);283 expect(data.raw.mode).to.be.eq('ReFungible');284285 const options = await collection.getOptions();286287 expect(options.tokenPropertyPermissions).to.be.empty;288 });289290 itEth('Create collection with properties & get description', async ({helper}) => {291 const owner = await helper.eth.createAccountWithBalance(donor);292293 const name = 'CollectionEVM';294 const description = 'Some description';295 const prefix = 'token prefix';296 const baseUri = 'BaseURI';297298 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, name, description, prefix, baseUri);299 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');300301 const collection = helper.rft.getCollectionObject(collectionId);302 const data = (await collection.getData())!;303304 expect(data.name).to.be.eq(name);305 expect(data.description).to.be.eq(description);306 expect(data.raw.tokenPrefix).to.be.eq(prefix);307 expect(data.raw.mode).to.be.eq('ReFungible');308309 expect(await contract.methods.description().call()).to.deep.equal(description);310311 const options = await collection.getOptions();312 expect(options.tokenPropertyPermissions).to.be.deep.equal([313 {314 key: 'URI',315 permission: {mutable: true, collectionAdmin: true, tokenOwner: false},316 },317 {318 key: 'URISuffix',319 permission: {mutable: true, collectionAdmin: true, tokenOwner: false},320 },321 ]);322 });323324 itEth('Set sponsorship', async ({helper}) => {325 const owner = await helper.eth.createAccountWithBalance(donor);326 const sponsor = await helper.eth.createAccountWithBalance(donor);327 const ss58Format = helper.chain.getChainProperties().ss58Format;328 const {collectionId, collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Sponsor', 'absolutely anything', 'ENVY', 'rft')).send();329330 const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);331 const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);332 await collection.methods.setCollectionSponsorCross(sponsorCross).send();333334 let data = (await helper.rft.getData(collectionId))!;335 expect(data.raw.sponsorship.Unconfirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));336337 await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');338339 const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);340 await sponsorCollection.methods.confirmCollectionSponsorship().send();341342 data = (await helper.rft.getData(collectionId))!;343 expect(data.raw.sponsorship.Confirmed).to.be.equal(evmToAddress(sponsor, Number(ss58Format)));344 });345346 itEth('Collection address exist', async ({helper}) => {347 const owner = await helper.eth.createAccountWithBalance(donor);348 const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';349 const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);350351 expect(await collectionHelpers352 .methods.isCollectionExist(collectionAddressForNonexistentCollection).call())353 .to.be.false;354355 const {collectionAddress} = await helper.eth.createCollection(owner, new CreateCollectionData('Exister', 'absolutely anything', 'WIWT', 'rft')).send();356 expect(await collectionHelpers357 .methods.isCollectionExist(collectionAddress).call())358 .to.be.true;359360 // check collectionOwner:361 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);362 const collectionOwner = await collectionEvm.methods.collectionOwner().call();363 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));364 });365366 itEth('destroyCollection', async ({helper}) => {367 const owner = await helper.eth.createAccountWithBalance(donor);368 const {collectionAddress, collectionId} = await helper.eth.createCollection(owner, new CreateCollectionData('Limits', 'absolutely anything', 'OLF', 'rft')).send();369 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);370371 await expect(collectionHelper.methods372 .destroyCollection(collectionAddress)373 .send({from: owner})).to.be.fulfilled;374375 expect(await collectionHelper.methods376 .isCollectionExist(collectionAddress)377 .call()).to.be.false;378 expect(await helper.collection.getData(collectionId)).to.be.null;379 });380381 itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {382 const owner = await helper.eth.createAccountWithBalance(donor);383 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);384 {385 const MAX_NAME_LENGTH = 64;386 const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);387 const description = 'A';388 const tokenPrefix = 'A';389390 await expect(collectionHelper.methods391 .createCollection([392 collectionName,393 description,394 tokenPrefix,395 CollectionMode.Refungible,396 DECIMALS,397 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,398 ])399 .call({value: Number(2n * nominal)})).to.be.rejectedWith('name is too long. Max length is ' + MAX_NAME_LENGTH);400 }401 {402 const MAX_DESCRIPTION_LENGTH = 256;403 const collectionName = 'A';404 const description = 'A'.repeat(MAX_DESCRIPTION_LENGTH + 1);405 const tokenPrefix = 'A';406 await expect(collectionHelper.methods407 .createCollection([408 collectionName,409 description,410 tokenPrefix,411 CollectionMode.Refungible,412 DECIMALS,413 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,414 ])415 .call({value: Number(2n * nominal)})).to.be.rejectedWith('description is too long. Max length is ' + MAX_DESCRIPTION_LENGTH);416 }417 {418 const MAX_TOKEN_PREFIX_LENGTH = 16;419 const collectionName = 'A';420 const description = 'A';421 const tokenPrefix = 'A'.repeat(MAX_TOKEN_PREFIX_LENGTH + 1);422 await expect(collectionHelper.methods423 .createCollection([424 collectionName,425 description,426 tokenPrefix,427 CollectionMode.Refungible,428 DECIMALS,429 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,430 ])431 .call({value: Number(2n * nominal)})).to.be.rejectedWith('token_prefix is too long. Max length is ' + MAX_TOKEN_PREFIX_LENGTH);432 }433 });434435 itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {436 const owner = await helper.eth.createAccountWithBalance(donor);437 const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);438 await expect(collectionHelper.methods439 .createCollection([440 'Peasantry',441 'absolutely anything',442 'TWIW',443 CollectionMode.Refungible,444 0,445 ...CREATE_COLLECTION_DATA_DEFAULTS_ARRAY,446 ])447 .call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');448 });449 });450451 describe('Sponsoring', () => {452 itEth('Сan remove collection sponsor', async ({helper}) => {453 const owner = await helper.eth.createAccountWithBalance(donor);454 const sponsor = await helper.eth.createAccountWithBalance(donor);455 const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);456457 const {collectionAddress} = await helper.eth.createCollection(458 owner,459 {460 ...CREATE_COLLECTION_DATA_DEFAULTS,461 name: 'Sponsor collection',462 description: '1',463 tokenPrefix: '1',464 collectionMode: 'nft',465 pendingSponsor: sponsorCross,466 },467 ).send();468 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);469470 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.true;471472 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});473 let sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});474 expect(helper.address.restoreCrossAccountFromBigInt(BigInt(sponsorStruct.sub))).to.be.eq(helper.address.ethToSubstrate(sponsor, true));475 expect(await collectionEvm.methods.hasCollectionPendingSponsor().call({from: owner})).to.be.false;476477 await collectionEvm.methods.removeCollectionSponsor().send({from: owner});478479 sponsorStruct = await collectionEvm.methods.collectionSponsor().call({from: owner});480 expect(sponsorStruct.eth).to.be.eq('0x0000000000000000000000000000000000000000');481 });482483 itEth('Can sponsor from evm address via access list', async ({helper}) => {484 const owner = await helper.eth.createAccountWithBalance(donor);485 const sponsorEth = await helper.eth.createAccountWithBalance(donor);486 const sponsorCross = helper.ethCrossAccount.fromAddr(sponsorEth);487488 const {collectionId, collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(489 owner,490 {491 ...CREATE_COLLECTION_DATA_DEFAULTS,492 name: 'Sponsor collection',493 description: '1',494 tokenPrefix: '1',495 collectionMode: 'nft',496 pendingSponsor: sponsorCross,497 limits: [{field: CollectionLimitField.SponsoredDataRateLimit, value: 30n}],498 tokenPropertyPermissions: [{key: 'key', permissions: [{code: TokenPermissionField.TokenOwner, value: true}]}],499 },500 '',501 );502503 const collectionSub = helper.nft.getCollectionObject(collectionId);504 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);505506 let sponsorship = (await collectionSub.getData())!.raw.sponsorship;507 expect(sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));508 // Account cannot confirm sponsorship if it is not set as a sponsor509 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');510511 // Sponsor can confirm sponsorship:512 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});513 sponsorship = (await collectionSub.getData())!.raw.sponsorship;514 expect(sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsorEth, true));515516 // Create user with no balance:517 const user = helper.ethCrossAccount.createAccount();518 const nextTokenId = await collectionEvm.methods.nextTokenId().call();519 expect(nextTokenId).to.be.equal('1');520521 // Set collection permissions:522 const oldPermissions = (await collectionSub.getData())!.raw.permissions;523 expect(oldPermissions.mintMode).to.be.false;524 expect(oldPermissions.access).to.be.equal('Normal');525526 await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});527 await collectionEvm.methods.addToCollectionAllowListCross(user).send({from: owner});528 await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});529530 const newPermissions = (await collectionSub.getData())!.raw.permissions;531 expect(newPermissions.mintMode).to.be.true;532 expect(newPermissions.access).to.be.equal('AllowList');533534 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));535 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));536 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));537538 // User can mint token without balance:539 {540 const result = await collectionEvm.methods.mintCross(user, [{key: 'key', value: Buffer.from('Value')}]).send({from: user.eth});541 const event = helper.eth.normalizeEvents(result.events)542 .find(event => event.event === 'Transfer');543544 expect(event).to.be.deep.equal({545 address: collectionAddress,546 event: 'Transfer',547 args: {548 from: '0x0000000000000000000000000000000000000000',549 to: user.eth,550 tokenId: '1',551 },552 });553554 // await collectionEvm.methods.setProperties(1, [{key: 'key', value: Buffer.from('Value1')}]).send({from: user.eth});555556 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));557 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsorEth));558 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user.eth));559560 expect(await collectionEvm.methods.properties(nextTokenId, []).call())561 .to.be.like([562 [563 'key',564 '0x' + Buffer.from('Value').toString('hex'),565 ],566 ]);567 expect(ownerBalanceBefore).to.be.eq(ownerBalanceAfter);568 expect(userBalanceAfter).to.be.eq(userBalanceBefore);569 expect(sponsorBalanceBefore > sponsorBalanceAfter).to.be.true;570 }571 });572573 itEth('Check that transaction via EVM spend money from sponsor address', async ({helper}) => {574 const owner = await helper.eth.createAccountWithBalance(donor);575 const sponsor = await helper.eth.createAccountWithBalance(donor);576 const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);577 const user = helper.eth.createAccount();578 const userCross = helper.ethCrossAccount.fromAddress(user);579580 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(581 owner,582 {583 ...CREATE_COLLECTION_DATA_DEFAULTS,584 name: 'Sponsor collection',585 description: '1',586 tokenPrefix: '1',587 collectionMode: 'nft',588 pendingSponsor: sponsorCross,589 adminList: [userCross],590 },591 '',592 );593594 const collectionSub = helper.nft.getCollectionObject(collectionId);595 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);596 // Set collection sponsor:597 let collectionData = (await collectionSub.getData())!;598 expect(collectionData.raw.sponsorship.Unconfirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));599 await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');600601 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});602 collectionData = (await collectionSub.getData())!;603 expect(collectionData.raw.sponsorship.Confirmed).to.be.eq(helper.address.ethToSubstrate(sponsor, true));604605 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));606 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));607608 const mintingResult = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});609 const tokenId = mintingResult.events.Transfer.returnValues.tokenId;610611 const event = helper.eth.normalizeEvents(mintingResult.events)612 .find(event => event.event === 'Transfer');613 const address = helper.ethAddress.fromCollectionId(collectionId);614615 expect(event).to.be.deep.equal({616 address,617 event: 'Transfer',618 args: {619 from: '0x0000000000000000000000000000000000000000',620 to: user,621 tokenId: '1',622 },623 });624 expect(await collectionEvm.methods.tokenURI(tokenId).call({from: user})).to.be.equal('Test URI');625626 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));627 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);628 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));629 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;630 });631632 itEth('Can reassign collection sponsor', async ({helper}) => {633 const owner = await helper.eth.createAccountWithBalance(donor);634 const sponsorEth = await helper.eth.createAccountWithBalance(donor);635 const sponsorCrossEth = helper.ethCrossAccount.fromAddr(sponsorEth);636 const [sponsorSub] = await helper.arrange.createAccounts([100n], donor);637 const sponsorCrossSub = helper.ethCrossAccount.fromKeyringPair(sponsorSub);638639 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(640 owner,641 {642 ...CREATE_COLLECTION_DATA_DEFAULTS,643 name: 'Sponsor collection',644 description: '1',645 tokenPrefix: '1',646 collectionMode: 'nft',647 pendingSponsor: sponsorCrossEth,648 },649 '',650 );651 const collectionSub = helper.nft.getCollectionObject(collectionId);652 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);653654 // Set and confirm sponsor:655 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsorEth});656657 // Can reassign sponsor:658 await collectionEvm.methods.setCollectionSponsorCross(sponsorCrossSub).send({from: owner});659 const collectionSponsor = (await collectionSub.getData())?.raw.sponsorship;660 expect(collectionSponsor).to.deep.eq({Unconfirmed: sponsorSub.address});661 });662663 [664 'transfer',665 'transferCross',666 'transferFrom',667 'transferFromCross',668 ].map(testCase =>669 itEth(`[${testCase}] Check that transfer via EVM spend money from sponsor address`, async ({helper}) => {670 const owner = await helper.eth.createAccountWithBalance(donor);671 const sponsor = await helper.eth.createAccountWithBalance(donor);672 const sponsorCross = helper.ethCrossAccount.fromAddr(sponsor);673 const user = await helper.eth.createAccountWithBalance(donor);674 const userCross = helper.ethCrossAccount.fromAddress(user);675676 const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleCollection(677 owner,678 {679 ...CREATE_COLLECTION_DATA_DEFAULTS,680 name: 'Sponsor collection',681 description: '1',682 tokenPrefix: '1',683 collectionMode: 'rft',684 pendingSponsor: sponsorCross,685 adminList: [userCross],686 },687 '',688 );689 const receiver = await helper.eth.createAccountWithBalance(donor);690 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);691692 await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});693694 const result = await collectionEvm.methods.mintWithTokenURI(user, 'Test URI').send({from: user});695 const tokenId = result.events.Transfer.returnValues.tokenId;696697 const ownerBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));698 const sponsorBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));699 const userBalanceBefore = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));700701 switch (testCase) {702 case 'transfer':703 await collectionEvm.methods.transfer(receiver, tokenId).send({from: user});704 break;705 case 'transferCross':706 await collectionEvm.methods.transferCross(helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});707 break;708 case 'transferFrom':709 await collectionEvm.methods.transferFrom(user, receiver, tokenId).send({from: user});710 break;711 case 'transferFromCross':712 await collectionEvm.methods.transferFromCross(helper.ethCrossAccount.fromAddress(user), helper.ethCrossAccount.fromAddress(receiver), tokenId).send({from: user});713 break;714 }715716 const ownerBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(owner));717 expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);718 const sponsorBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(sponsor));719 expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;720 const userBalanceAfter = await helper.balance.getSubstrate(helper.address.ethToSubstrate(user));721 expect(userBalanceAfter).to.be.eq(userBalanceBefore);722 }));723 });724725 describe('Collection admins', () => {726 let donor: IKeyringPair;727728 before(async function() {729 await usingEthPlaygrounds(async (_helper, privateKey) => {730 donor = await privateKey({url: import.meta.url});731 });732 });733734 [735 {mode: 'nft' as const, requiredPallets: []},736 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},737 {mode: 'ft' as const, requiredPallets: []},738 ].map(testCase => {739 itEth.ifWithPallets(`can add account admin by owner for ${testCase.mode}`, testCase.requiredPallets, async ({helper, privateKey}) => {740 // arrange741 const owner = await helper.eth.createAccountWithBalance(donor);742 const adminSub = await privateKey('//admin2');743 const adminEth = helper.eth.createAccount().toLowerCase();744745 const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);746 const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);747748 const {collectionAddress, collectionId} = await helper.eth.createCollection(749 owner,750 {751 ...CREATE_COLLECTION_DATA_DEFAULTS,752 name: 'Mint collection',753 description: 'a',754 tokenPrefix: 'b',755 collectionMode: testCase.mode,756 adminList: [adminCrossSub, adminCrossEth],757 },758 ).send();759 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner, true);760761 // 1. Expect api.rpc.unique.adminlist returns admins:762 const adminListRpc = await helper.collection.getAdmins(collectionId);763 expect(adminListRpc).to.has.length(2);764 expect(adminListRpc).to.be.deep.contain.members([{Substrate: adminSub.address}, {Ethereum: adminEth}]);765766 // 2. Expect methods.collectionAdmins == api.rpc.unique.adminlist767 let adminListEth = await collectionEvm.methods.collectionAdmins().call();768 adminListEth = adminListEth.map((element: IEthCrossAccountId) => helper.address.convertCrossAccountFromEthCrossAccount(element));769 expect(adminListRpc).to.be.like(adminListEth);770771 // 3. check isOwnerOrAdminCross returns true:772 expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossSub).call()).to.be.true;773 expect(await collectionEvm.methods.isOwnerOrAdminCross(adminCrossEth).call()).to.be.true;774 });775 });776777 itEth('cross account admin can mint', async ({helper}) => {778 // arrange: create collection and accounts779 const owner = await helper.eth.createAccountWithBalance(donor);780 const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();781 const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);782 const [adminSub] = await helper.arrange.createAccounts([100n], donor);783 const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);784 const {collectionAddress, collectionId} = await helper.eth.createERC721MetadataCompatibleCollection(785 owner,786 {787 ...CREATE_COLLECTION_DATA_DEFAULTS,788 name: 'Mint collection',789 description: 'a',790 tokenPrefix: 'b',791 collectionMode: 'nft',792 adminList: [adminCrossSub, adminCrossEth],793 },794 'uri',795 );796 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);797798 // admin (sub and eth) can mint token:799 await collectionEvm.methods.mint(owner).send({from: adminEth});800 await helper.nft.mintToken(adminSub, {collectionId, owner: {Ethereum: owner}});801802 expect(await helper.collection.getLastTokenId(collectionId)).to.eq(2);803 });804805 itEth('cannot add invalid cross account admin', async ({helper}) => {806 const owner = await helper.eth.createAccountWithBalance(donor);807 const [admin] = await helper.arrange.createAccounts([100n, 100n], donor);808809 const adminCross = {810 eth: helper.address.substrateToEth(admin.address),811 sub: admin.addressRaw,812 };813814 await expect(helper.eth.createCollection(815 owner,816 {817 ...CREATE_COLLECTION_DATA_DEFAULTS,818 name: 'A',819 description: 'B',820 tokenPrefix: 'C',821 collectionMode: 'nft',822 adminList: [adminCross],823 },824 ).call()).to.be.rejected;825 });826827 itEth('Remove [cross] admin by owner', async ({helper}) => {828 const owner = await helper.eth.createAccountWithBalance(donor);829830 const [adminSub] = await helper.arrange.createAccounts([10n], donor);831 const adminEth = (await helper.eth.createAccountWithBalance(donor)).toLowerCase();832 const adminCrossSub = helper.ethCrossAccount.fromKeyringPair(adminSub);833 const adminCrossEth = helper.ethCrossAccount.fromAddress(adminEth);834835 const {collectionAddress, collectionId} = await helper.eth.createCollection(836 owner,837 {838 ...CREATE_COLLECTION_DATA_DEFAULTS,839 name: 'A',840 description: 'B',841 tokenPrefix: 'C',842 collectionMode: 'nft',843 adminList: [adminCrossSub, adminCrossEth],844 },845 ).send();846847 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);848849 {850 const adminList = await helper.collection.getAdmins(collectionId);851 expect(adminList).to.deep.include({Substrate: adminSub.address});852 expect(adminList).to.deep.include({Ethereum: adminEth});853 }854855 await collectionEvm.methods.removeCollectionAdminCross(adminCrossSub).send();856 await collectionEvm.methods.removeCollectionAdminCross(adminCrossEth).send();857 const adminList = await helper.collection.getAdmins(collectionId);858 expect(adminList.length).to.be.eq(0);859860 // Non admin cannot mint:861 await expect(helper.nft.mintToken(adminSub, {collectionId, owner: {Substrate: adminSub.address}})).to.be.rejectedWith(/common.PublicMintingNotAllowed/);862 await expect(collectionEvm.methods.mint(adminEth).send({from: adminEth})).to.be.rejected;863 });864 });865866 describe('Collection limits', () => {867 describe('Can set collection limits', () => {868 [869 {case: 'nft' as const},870 {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},871 {case: 'ft' as const},872 ].map(testCase =>873 itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {874 const owner = await helper.eth.createAccountWithBalance(donor);875876 const limits = {877 accountTokenOwnershipLimit: 1000n,878 sponsoredDataSize: 1024n,879 sponsoredDataRateLimit: 30n,880 tokenLimit: 1000000n,881 sponsorTransferTimeout: 6n,882 sponsorApproveTimeout: 6n,883 ownerCanTransfer: 1n,884 ownerCanDestroy: 0n,885 transfersEnabled: 0n,886 };887888 const {collectionId, collectionAddress} = await helper.eth.createCollection(889 owner,890 {891 ...CREATE_COLLECTION_DATA_DEFAULTS,892 name: 'Limits',893 description: 'absolutely anything',894 tokenPrefix: 'FLO',895 collectionMode: testCase.case,896 limits: [897 {field: CollectionLimitField.AccountTokenOwnership, value: limits.accountTokenOwnershipLimit},898 {field: CollectionLimitField.SponsoredDataSize, value: limits.sponsoredDataSize},899 {field: CollectionLimitField.SponsoredDataRateLimit, value: limits.sponsoredDataRateLimit},900 {field: CollectionLimitField.TokenLimit, value: limits.tokenLimit},901 {field: CollectionLimitField.SponsorTransferTimeout, value: limits.sponsorTransferTimeout},902 {field: CollectionLimitField.SponsorApproveTimeout, value: limits.sponsorApproveTimeout},903 {field: CollectionLimitField.OwnerCanTransfer, value: limits.ownerCanTransfer},904 {field: CollectionLimitField.OwnerCanDestroy, value: limits.ownerCanDestroy},905 {field: CollectionLimitField.TransferEnabled, value: limits.transfersEnabled},906 ],907 },908 ).send();909910 const expectedLimits = {911 accountTokenOwnershipLimit: 1000,912 sponsoredDataSize: 1024,913 sponsoredDataRateLimit: {blocks: 30},914 tokenLimit: 1000000,915 sponsorTransferTimeout: 6,916 sponsorApproveTimeout: 6,917 ownerCanTransfer: true,918 ownerCanDestroy: false,919 transfersEnabled: false,920 };921922 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.case, owner);923924 // Check limits from sub:925 const data = (await helper.rft.getData(collectionId))!;926 expect(data.raw.limits).to.deep.eq(expectedLimits);927 expect(await helper.collection.getEffectiveLimits(collectionId)).to.deep.eq(expectedLimits);928 // Check limits from eth:929 const limitsEvm = await collectionEvm.methods.collectionLimits().call({from: owner});930 expect(limitsEvm).to.have.length(9);931 expect(limitsEvm[0]).to.deep.eq([CollectionLimitField.AccountTokenOwnership.toString(), [true, limits.accountTokenOwnershipLimit.toString()]]);932 expect(limitsEvm[1]).to.deep.eq([CollectionLimitField.SponsoredDataSize.toString(), [true, limits.sponsoredDataSize.toString()]]);933 expect(limitsEvm[2]).to.deep.eq([CollectionLimitField.SponsoredDataRateLimit.toString(), [true, limits.sponsoredDataRateLimit.toString()]]);934 expect(limitsEvm[3]).to.deep.eq([CollectionLimitField.TokenLimit.toString(), [true, limits.tokenLimit.toString()]]);935 expect(limitsEvm[4]).to.deep.eq([CollectionLimitField.SponsorTransferTimeout.toString(), [true, limits.sponsorTransferTimeout.toString()]]);936 expect(limitsEvm[5]).to.deep.eq([CollectionLimitField.SponsorApproveTimeout.toString(), [true, limits.sponsorApproveTimeout.toString()]]);937 expect(limitsEvm[6]).to.deep.eq([CollectionLimitField.OwnerCanTransfer.toString(), [true, limits.ownerCanTransfer.toString()]]);938 expect(limitsEvm[7]).to.deep.eq([CollectionLimitField.OwnerCanDestroy.toString(), [true, limits.ownerCanDestroy.toString()]]);939 expect(limitsEvm[8]).to.deep.eq([CollectionLimitField.TransferEnabled.toString(), [true, limits.transfersEnabled.toString()]]);940 }));941 });942943 describe('(!negative test!) Cannot set invalid collection limits', () => {944 [945 {case: 'nft' as const},946 {case: 'rft' as const, requiredPallets: [Pallets.ReFungible]},947 {case: 'ft' as const},948 ].map(testCase =>949 itEth.ifWithPallets(`for ${testCase.case}`, testCase.requiredPallets || [], async ({helper}) => {950 const invalidLimits = {951 accountTokenOwnershipLimit: BigInt(Number.MAX_SAFE_INTEGER),952 transfersEnabled: 3,953 };954955 const createCollectionData = {956 ...CREATE_COLLECTION_DATA_DEFAULTS,957 name: 'Limits',958 description: 'absolutely anything',959 tokenPrefix: 'ISNI',960 collectionMode: testCase.case,961 };962963 const owner = await helper.eth.createAccountWithBalance(donor);964 await expect(helper.eth.createCollection(965 owner,966 {967 ...createCollectionData,968 limits: [{field: 9 as CollectionLimitField, value: 1n}],969 },970 ).call()).to.be.rejectedWith('value not convertible into enum "CollectionLimitField"');971972 await expect(helper.eth.createCollection(973 owner,974 {975 ...createCollectionData,976 limits: [{field: CollectionLimitField.AccountTokenOwnership, value: invalidLimits.accountTokenOwnershipLimit}],977 },978 ).call()).to.be.rejectedWith(`can't convert value to u32 "${invalidLimits.accountTokenOwnershipLimit}"`);979980 await expect(helper.eth.createCollection(981 owner,982 {983 ...createCollectionData,984 limits: [{field: CollectionLimitField.TransferEnabled, value: 3n}],985 },986 ).call()).to.be.rejectedWith(`can't convert value to boolean "${invalidLimits.transfersEnabled}"`);987988 await expect(helper.eth.createCollection(989 owner,990 {991 ...createCollectionData,992 limits: [{field: CollectionLimitField.SponsoredDataSize, value: -1n}],993 },994 ).call()).to.be.rejectedWith('value out-of-bounds');995 }));996 });997 });998999 describe('Collection properties', () => {10001001 [1002 {mode: 'nft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1003 {mode: 'rft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1004 {mode: 'ft' as const, methodParams: [{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}], expectedProps: [{key: 'testKey1', value: 'testValue1'}, {key: 'testKey2', value: 'testValue2'}]},1005 ].map(testCase =>1006 itEth.ifWithPallets(`Collection properties can be set for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {1007 const caller = await helper.eth.createAccountWithBalance(donor);1008 const callerCross = helper.ethCrossAccount.fromAddress(caller);1009 const owner = await helper.eth.createAccountWithBalance(donor);1010 const {collectionId, collectionAddress} = await helper.eth.createCollection(1011 owner,1012 {1013 ...CREATE_COLLECTION_DATA_DEFAULTS,1014 name: 'name',1015 description: 'test',1016 tokenPrefix: 'test',1017 collectionMode: testCase.mode,1018 adminList: [callerCross],1019 properties: testCase.methodParams,1020 },1021 ).send();10221023 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);10241025 const raw = (await helper[testCase.mode].getData(collectionId))?.raw;1026 expect(raw.properties).to.deep.equal(testCase.expectedProps);10271028 // collectionProperties returns properties:1029 expect(await collectionEvm.methods.collectionProperties([]).call()).to.be.like(testCase.expectedProps.map(prop => helper.ethProperty.property(prop.key, prop.value)));1030 }));10311032 [1033 {mode: 'nft' as const},1034 {mode: 'rft' as const},1035 {mode: 'ft' as const},1036 ].map(testCase =>1037 itEth.ifWithPallets(`Collection properties can be deleted for ${testCase.mode}`, testCase.mode === 'rft' ? [Pallets.ReFungible] : [], async({helper}) => {1038 const caller = await helper.eth.createAccountWithBalance(donor);1039 const callerCross = helper.ethCrossAccount.fromAddress(caller);1040 const owner = await helper.eth.createAccountWithBalance(donor);1041 const {collectionId, collectionAddress} = await helper.eth.createCollection(1042 owner,1043 {1044 ...CREATE_COLLECTION_DATA_DEFAULTS,1045 name: 'name',1046 description: 'test',1047 tokenPrefix: 'test',1048 collectionMode: testCase.mode,1049 adminList: [callerCross],1050 properties:[1051 {key: 'testKey1', value: Buffer.from('testValue1')},1052 {key: 'testKey2', value: Buffer.from('testValue2')},1053 {key: 'testKey3', value: Buffer.from('testValue3')}],1054 },1055 ).send();10561057 const collectionEvm = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);10581059 await collectionEvm.methods.deleteCollectionProperties(['testKey1', 'testKey2']).send({from: caller});10601061 const raw = (await helper[testCase.mode].getData(collectionId))?.raw;10621063 expect(raw.properties.length).to.equal(1);1064 expect(raw.properties).to.deep.equal([{key: 'testKey3', value: 'testValue3'}]);1065 }));10661067 itEth('(!negative test!) Cannot set invalid properties', async({helper}) => {1068 const caller = await helper.eth.createAccountWithBalance(donor);1069 const callerCross = helper.ethCrossAccount.fromAddress(caller);1070 const owner = await helper.eth.createAccountWithBalance(donor);1071 const createCollectionData = {1072 ...CREATE_COLLECTION_DATA_DEFAULTS,1073 name: 'name',1074 description: 'test',1075 tokenPrefix: 'test',1076 collectionMode: 'nft' as TCollectionMode,1077 adminList: [callerCross],1078 };1079 await expect(helper.eth.createCollection(1080 owner,1081 {1082 ...createCollectionData,1083 properties: [{key: '', value: Buffer.from('val1')}],1084 },1085 ).call()).to.be.rejected;10861087 await expect(helper.eth.createCollection(1088 owner,1089 {1090 ...createCollectionData,1091 properties: [{key: 'déjà vu', value: Buffer.from('hmm...')}],1092 },1093 ).call()).to.be.rejected;10941095 await expect(helper.eth.createCollection(1096 owner,1097 {1098 ...createCollectionData,1099 properties: [{key: 'a'.repeat(257), value: Buffer.from('val3')}],1100 },1101 ).call()).to.be.rejected;1102 });11031104 itEth('(!negative test!) cannot delete properties of non-owned collections', async ({helper}) => {1105 const caller = await helper.eth.createAccountWithBalance(donor);1106 const owner = await helper.eth.createAccountWithBalance(donor);1107 const {collectionAddress} = await helper.eth.createCollection(1108 owner,1109 {1110 ...CREATE_COLLECTION_DATA_DEFAULTS,1111 name: 'name',1112 description: 'test',1113 tokenPrefix: 'test',1114 collectionMode: 'nft',1115 properties:[1116 {key: 'testKey1', value: Buffer.from('testValue1')},1117 {key: 'testKey2', value: Buffer.from('testValue2')}],1118 },1119 ).send();11201121 const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', caller);11221123 await expect(collectionEvm.methods.deleteCollectionProperties(['testKey2']).send({from: caller})).to.be.rejected;1124 });1125 });11261127 describe('Token property permissions', () => {1128 [1129 {mode: 'nft' as const, requiredPallets: []},1130 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1131 ].map(testCase =>1132 itEth.ifWithPallets(`[${testCase.mode}] Can set all possible token property permissions`, testCase.requiredPallets, async({helper}) => {1133 const owner = await helper.eth.createAccountWithBalance(donor);1134 const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);1135 for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {1136 const {collectionId, collectionAddress} = await helper.eth.createCollection(1137 owner,1138 {1139 ...CREATE_COLLECTION_DATA_DEFAULTS,1140 name: 'A',1141 description: 'B',1142 tokenPrefix: 'C',1143 collectionMode: testCase.mode,1144 adminList: [caller],1145 tokenPropertyPermissions: [1146 {1147 key: 'testKey',1148 permissions: [1149 {code: TokenPermissionField.Mutable, value: mutable},1150 {code: TokenPermissionField.TokenOwner, value: tokenOwner},1151 {code: TokenPermissionField.CollectionAdmin, value: collectionAdmin},1152 ],1153 },1154 ],1155 },1156 ).send();1157 const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);11581159 expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{1160 key: 'testKey',1161 permission: {mutable, collectionAdmin, tokenOwner},1162 }]);11631164 expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([1165 ['testKey', [1166 [TokenPermissionField.Mutable.toString(), mutable],1167 [TokenPermissionField.TokenOwner.toString(), tokenOwner],1168 [TokenPermissionField.CollectionAdmin.toString(), collectionAdmin]],1169 ],1170 ]);1171 }1172 }));11731174 [1175 {mode: 'nft' as const, requiredPallets: []},1176 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1177 ].map(testCase =>1178 itEth.ifWithPallets(`[${testCase.mode}] Can set multiple token property permissions`, testCase.requiredPallets, async({helper}) => {1179 const owner = await helper.eth.createAccountWithBalance(donor);1180 const {collectionId, collectionAddress} = await helper.eth.createCollection(1181 owner,1182 {1183 ...CREATE_COLLECTION_DATA_DEFAULTS,1184 name: 'A',1185 description: 'B',1186 tokenPrefix: 'C',1187 collectionMode: testCase.mode,1188 tokenPropertyPermissions: [1189 {1190 key: 'testKey_0',1191 permissions: [1192 {code: TokenPermissionField.Mutable, value: true},1193 {code: TokenPermissionField.TokenOwner, value: true},1194 {code: TokenPermissionField.CollectionAdmin, value: true}],1195 },1196 {1197 key: 'testKey_1',1198 permissions: [1199 {code: TokenPermissionField.Mutable, value: true},1200 {code: TokenPermissionField.TokenOwner, value: false},1201 {code: TokenPermissionField.CollectionAdmin, value: true}],1202 },1203 {1204 key: 'testKey_2',1205 permissions: [1206 {code: TokenPermissionField.Mutable, value: false},1207 {code: TokenPermissionField.TokenOwner, value: true},1208 {code: TokenPermissionField.CollectionAdmin, value: false}],1209 },1210 ],1211 },1212 ).send();1213 const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);12141215 expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([1216 {1217 key: 'testKey_0',1218 permission: {mutable: true, tokenOwner: true, collectionAdmin: true},1219 },1220 {1221 key: 'testKey_1',1222 permission: {mutable: true, tokenOwner: false, collectionAdmin: true},1223 },1224 {1225 key: 'testKey_2',1226 permission: {mutable: false, tokenOwner: true, collectionAdmin: false},1227 },1228 ]);12291230 expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([1231 ['testKey_0', [1232 [TokenPermissionField.Mutable.toString(), true],1233 [TokenPermissionField.TokenOwner.toString(), true],1234 [TokenPermissionField.CollectionAdmin.toString(), true]],1235 ],1236 ['testKey_1', [1237 [TokenPermissionField.Mutable.toString(), true],1238 [TokenPermissionField.TokenOwner.toString(), false],1239 [TokenPermissionField.CollectionAdmin.toString(), true]],1240 ],1241 ['testKey_2', [1242 [TokenPermissionField.Mutable.toString(), false],1243 [TokenPermissionField.TokenOwner.toString(), true],1244 [TokenPermissionField.CollectionAdmin.toString(), false]],1245 ],1246 ]);1247 }));12481249 [1250 {mode: 'nft' as const, requiredPallets: []},1251 {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},1252 ].map(testCase =>1253 itEth.ifWithPallets(`Can be deleted for ${testCase.mode}`, testCase.requiredPallets, async({helper}) => {1254 const caller = await helper.eth.createAccountWithBalance(donor);1255 const receiver = await helper.ethCrossAccount.createAccountWithBalance(donor);1256 const {collectionAddress} = await helper.eth.createCollection(1257 caller,1258 {1259 ...CREATE_COLLECTION_DATA_DEFAULTS,1260 name: 'A',1261 description: 'B',1262 tokenPrefix: 'C',1263 collectionMode: testCase.mode,1264 adminList: [receiver],1265 tokenPropertyPermissions: [1266 {1267 key: 'testKey',1268 permissions: [1269 {code: TokenPermissionField.Mutable, value: true},1270 {code: TokenPermissionField.CollectionAdmin, value: true}],1271 },1272 {1273 key: 'testKey_1',1274 permissions: [1275 {code: TokenPermissionField.Mutable, value: true},1276 {code: TokenPermissionField.CollectionAdmin, value: true}],1277 },1278 ],1279 },1280 ).send();12811282 const collection = helper.ethNativeContract.collection(collectionAddress, testCase.mode, caller);1283 const tokenId = (await collection.methods.mintCross(receiver, [{key: 'testKey', value: Buffer.from('testValue')}, {key: 'testKey_1', value: Buffer.from('testValue_1')}]).send()).events.Transfer.returnValues.tokenId;1284 expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(2);12851286 await collection.methods.deleteProperties(tokenId, ['testKey', 'testKey_1']).send({from: caller});1287 expect(await collection.methods.properties(tokenId, ['testKey', 'testKey_1']).call()).to.has.length(0);1288 }));1289 });12901291 describe('Nesting', () => {1292 itEth('NFT: allows an Owner to nest/unnest their token', async ({helper}) => {1293 const owner = await helper.eth.createAccountWithBalance(donor);1294 const {collectionAddress, collectionId} = await helper.eth.createCollection(1295 owner,1296 {1297 ...CREATE_COLLECTION_DATA_DEFAULTS,1298 name: 'A',1299 description: 'B',1300 tokenPrefix: 'C',1301 collectionMode: 'nft',1302 nestingSettings: {token_owner: true, collection_admin: false, restricted: []},1303 },1304 ).send();13051306 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13071308 // Create a token to be nested to1309 const mintingTargetNFTTokenIdResult = await contract.methods.mint(owner).send({from: owner});1310 const targetNFTTokenId = mintingTargetNFTTokenIdResult.events.Transfer.returnValues.tokenId;1311 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetNFTTokenId);13121313 // Create a nested token1314 const mintingFirstTokenIdResult = await contract.methods.mint(targetNftTokenAddress).send({from: owner});1315 const firstTokenId = mintingFirstTokenIdResult.events.Transfer.returnValues.tokenId;1316 expect(await contract.methods.ownerOf(firstTokenId).call()).to.be.equal(targetNftTokenAddress);13171318 // Create a token to be nested and nest1319 const mintingSecondTokenIdResult = await contract.methods.mint(owner).send({from: owner});1320 const secondTokenId = mintingSecondTokenIdResult.events.Transfer.returnValues.tokenId;13211322 await contract.methods.transfer(targetNftTokenAddress, secondTokenId).send({from: owner});1323 expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(targetNftTokenAddress);13241325 // Unnest token back1326 await contract.methods.transferFrom(targetNftTokenAddress, owner, secondTokenId).send({from: owner});1327 expect(await contract.methods.ownerOf(secondTokenId).call()).to.be.equal(owner);1328 });13291330 itEth('NFT: collectionNesting()', async ({helper}) => {1331 const owner = await helper.eth.createAccountWithBalance(donor);1332 const {collectionAddress: unnestedCollectionAddress} = await helper.eth.createCollection(1333 owner,1334 new CreateCollectionData('A', 'B', 'C', 'nft'),1335 ).send();13361337 const unnestedContract = helper.ethNativeContract.collection(unnestedCollectionAddress, 'nft', owner);1338 expect(await unnestedContract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);13391340 const {collectionAddress} = await helper.eth.createCollection(1341 owner,1342 {1343 ...CREATE_COLLECTION_DATA_DEFAULTS,1344 name: 'A',1345 description: 'B',1346 tokenPrefix: 'C',1347 collectionMode: 'nft',1348 nestingSettings: {token_owner: true, collection_admin: false, restricted: [unnestedCollectionAddress.toString()]},1349 },1350 ).send();13511352 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);1353 expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([true, false, [unnestedCollectionAddress.toString()]]);1354 await contract.methods.setCollectionNesting([false, false, []]).send({from: owner});1355 expect(await contract.methods.collectionNesting().call({from: owner})).to.be.like([false, false, []]);1356 });13571358 itEth('NFT: disallows to nest token if nesting is disabled', async ({helper}) => {1359 const owner = await helper.eth.createAccountWithBalance(donor);13601361 const {collectionId, collectionAddress} = await helper.eth.createCollection(1362 owner,1363 {1364 ...CREATE_COLLECTION_DATA_DEFAULTS,1365 name: 'A',1366 description: 'B',1367 tokenPrefix: 'C',1368 collectionMode: 'nft',1369 nestingSettings: {token_owner: false, collection_admin: false, restricted: []},1370 },1371 ).send();13721373 const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);13741375 // Create a token to nest into1376 const mintingTargetTokenIdResult = await contract.methods.mint(owner).send({from: owner});1377 const targetTokenId = mintingTargetTokenIdResult.events.Transfer.returnValues.tokenId;1378 const targetNftTokenAddress = helper.ethAddress.fromTokenId(collectionId, targetTokenId);13791380 // Create a token to nest1381 const mintingNftTokenIdResult = await contract.methods.mint(owner).send({from: owner});1382 const nftTokenId = mintingNftTokenIdResult.events.Transfer.returnValues.tokenId;13831384 // Try to nest1385 await expect(contract.methods1386 .transfer(targetNftTokenAddress, nftTokenId)1387 .call({from: owner})).to.be.rejectedWith('UserIsNotAllowedToNest');1388 });1389 });13901391 describe('Flags', () => {1392 const createCollectionData = {1393 ...CREATE_COLLECTION_DATA_DEFAULTS,1394 name: 'A',1395 description: 'B',1396 tokenPrefix: 'C',1397 collectionMode: 'nft' as TCollectionMode,1398 };13991400 itEth('NFT: use numbers for flags', async ({helper}) => {1401 const owner = await helper.eth.createAccountWithBalance(donor);14021403 {1404 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 0}).send();1405 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: false});1406 }14071408 {1409 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: 64}).send();1410 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1411 }1412 });14131414 itEth('NFT: can\'t set foreign flag number', async ({helper}) => {1415 const owner = await helper.eth.createAccountWithBalance(donor);14161417 {1418 await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 128}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);1419 }14201421 {1422 await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: 192}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);1423 }1424 });14251426 itEth('NFT: use enum for flags', async ({helper}) => {1427 const owner = await helper.eth.createAccountWithBalance(donor);14281429 {1430 const {collectionId} = await helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata]}).send();1431 expect((await helper.nft.getData(collectionId))?.raw.flags).to.be.deep.equal({foreign: false, erc721metadata: true});1432 }1433 });14341435 itEth('NFT: foreign flag enum is ignored', async ({helper}) => {1436 const owner = await helper.eth.createAccountWithBalance(donor);14371438 {1439 await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);1440 }14411442 {1443 await expect(helper.eth.createCollection(owner, {...createCollectionData, flags: [CollectionFlag.Erc721metadata | CollectionFlag.Foreign]}).call({from: owner})).to.be.rejectedWith(/internal flags were used/);1444 }1445 });1446 });1447});tests/src/eth/createFTCollection.seqtest.tsdiffbeforeafterboth--- a/tests/src/eth/createFTCollection.seqtest.ts
+++ b/tests/src/eth/createFTCollection.seqtest.ts
@@ -59,7 +59,7 @@
const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
- const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
expect(await collectionHelpers.methods
.isCollectionExist(expectedCollectionAddress)
tests/src/eth/createFTCollection.test.tsdiffbeforeafterboth--- a/tests/src/eth/createFTCollection.test.ts
+++ b/tests/src/eth/createFTCollection.test.ts
@@ -50,7 +50,7 @@
await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
await sponsorCollection.methods.confirmCollectionSponsorship().send();
data = (await helper.rft.getData(collectionId))!;
@@ -64,7 +64,7 @@
const description = 'absolutely anything';
const {collectionId, collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Sponsor', DECIMALS, description, 'ENVY');
- const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
await collection.methods.setCollectionSponsorCross(sponsorCross).send();
@@ -73,7 +73,7 @@
await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
await sponsorCollection.methods.confirmCollectionSponsorship().send();
data = (await helper.rft.getData(collectionId))!;
@@ -84,7 +84,7 @@
itEth('Collection address exist', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
- const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
expect(await collectionHelpers
.methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
@@ -96,7 +96,7 @@
.to.be.true;
// check collectionOwner:
- const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
const collectionOwner = await collectionEvm.methods.collectionOwner().call();
expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
});
@@ -104,7 +104,7 @@
itEth('destroyCollection', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress, collectionId} = await helper.eth.createFungibleCollection(owner, 'Exister', DECIMALS, 'absolutely anything', 'WIWT');
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const result = await collectionHelper.methods
.destroyCollection(collectionAddress)
@@ -143,7 +143,7 @@
itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
{
const MAX_NAME_LENGTH = 64;
const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
@@ -176,7 +176,7 @@
itEth('(!negative test!) cannot create collection if value !== 2', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const expects = [0n, 1n, 30n].map(async value => {
await expect(collectionHelper.methods
.createFTCollection('Peasantry', DECIMALS, 'absolutely anything', 'TWIW')
@@ -190,7 +190,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const peasant = helper.eth.createAccount();
const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE');
- const peasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'ft', peasant, true);
+ const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant, true);
const EXPECTED_ERROR = 'NoPermission';
{
const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -198,7 +198,7 @@
.setCollectionSponsor(sponsor)
.call()).to.be.rejectedWith(EXPECTED_ERROR);
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor, true);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor, true);
await expect(sponsorCollection.methods
.confirmCollectionSponsorship()
.call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -214,7 +214,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const peasant = helper.eth.createAccount();
const {collectionAddress} = await helper.eth.createFungibleCollection(owner, 'Transgressed', DECIMALS, 'absolutely anything', 'YVNE');
- const peasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'ft', peasant);
+ const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', peasant);
const EXPECTED_ERROR = 'NoPermission';
{
const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -223,7 +223,7 @@
.setCollectionSponsorCross(sponsorCross)
.call()).to.be.rejectedWith(EXPECTED_ERROR);
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'ft', sponsor);
await expect(sponsorCollection.methods
.confirmCollectionSponsorship()
.call()).to.be.rejectedWith('ConfirmSponsorshipFail');
tests/src/eth/createNFTCollection.seqtest.tsdiffbeforeafterboth--- a/tests/src/eth/createNFTCollection.seqtest.ts
+++ b/tests/src/eth/createNFTCollection.seqtest.ts
@@ -72,7 +72,7 @@
const expectedCollectionId = +(await helper.callRpc('api.rpc.unique.collectionStats')).created + 1;
const expectedCollectionAddress = helper.ethAddress.fromCollectionId(expectedCollectionId);
- const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
expect(await collectionHelpers.methods
.isCollectionExist(expectedCollectionAddress)
tests/src/eth/createNFTCollection.test.tsdiffbeforeafterboth--- a/tests/src/eth/createNFTCollection.test.ts
+++ b/tests/src/eth/createNFTCollection.test.ts
@@ -39,7 +39,7 @@
const baseUri = 'BaseURI';
const {collectionId, collectionAddress, events} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, name, description, prefix, baseUri);
- const contract = await helper.ethNativeContract.collection(collectionAddress, 'nft');
+ const contract = helper.ethNativeContract.collection(collectionAddress, 'nft');
expect(events).to.be.deep.equal([
{
@@ -82,7 +82,7 @@
const ss58Format = helper.chain.getChainProperties().ss58Format;
const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', 'absolutely anything', 'ROC');
- const collection = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
+ const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner, true);
await collection.methods.setCollectionSponsor(sponsor).send();
let data = (await helper.nft.getData(collectionId))!;
@@ -90,7 +90,7 @@
await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
await sponsorCollection.methods.confirmCollectionSponsorship().send();
data = (await helper.nft.getData(collectionId))!;
@@ -104,7 +104,7 @@
const description = 'absolutely anything';
const {collectionId, collectionAddress} = await helper.eth.createNFTCollection(owner, 'Sponsor', description, 'ROC');
- const collection = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
await collection.methods.setCollectionSponsorCross(sponsorCross).send();
@@ -125,7 +125,7 @@
itEth('Collection address exist', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
- const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
expect(await collectionHelpers
.methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
@@ -137,7 +137,7 @@
.to.be.true;
// check collectionOwner:
- const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
const collectionOwner = await collectionEvm.methods.collectionOwner().call();
expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
});
@@ -156,7 +156,7 @@
itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
{
const MAX_NAME_LENGTH = 64;
const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
@@ -190,7 +190,7 @@
itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
await expect(collectionHelper.methods
.createNFTCollection('Peasantry', 'absolutely anything', 'CVE')
.call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
@@ -209,7 +209,7 @@
.setCollectionSponsor(sponsor)
.call()).to.be.rejectedWith(EXPECTED_ERROR);
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor, true);
await expect(sponsorCollection.methods
.confirmCollectionSponsorship()
.call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -225,7 +225,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const malfeasant = helper.eth.createAccount();
const {collectionAddress} = await helper.eth.createNFTCollection(owner, 'Transgressed', 'absolutely anything', 'COR');
- const malfeasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);
+ const malfeasantCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', malfeasant);
const EXPECTED_ERROR = 'NoPermission';
{
const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -234,7 +234,7 @@
.setCollectionSponsorCross(sponsorCross)
.call()).to.be.rejectedWith(EXPECTED_ERROR);
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'nft', sponsor);
await expect(sponsorCollection.methods
.confirmCollectionSponsorship()
.call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -249,7 +249,7 @@
itEth('destroyCollection', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress, collectionId} = await helper.eth.createNFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
const result = await collectionHelper.methods
tests/src/eth/createRFTCollection.test.tsdiffbeforeafterboth--- a/tests/src/eth/createRFTCollection.test.ts
+++ b/tests/src/eth/createRFTCollection.test.ts
@@ -95,7 +95,7 @@
const ss58Format = helper.chain.getChainProperties().ss58Format;
const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
- const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true);
+ const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner, true);
await collection.methods.setCollectionSponsor(sponsor).send();
let data = (await helper.rft.getData(collectionId))!;
@@ -103,7 +103,7 @@
await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
await sponsorCollection.methods.confirmCollectionSponsorship().send();
data = (await helper.rft.getData(collectionId))!;
@@ -116,7 +116,7 @@
const ss58Format = helper.chain.getChainProperties().ss58Format;
const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sponsor', 'absolutely anything', 'ENVY');
- const collection = await helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
+ const collection = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);
const sponsorCross = helper.ethCrossAccount.fromAddress(sponsor);
await collection.methods.setCollectionSponsorCross(sponsorCross).send();
@@ -125,7 +125,7 @@
await expect(collection.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('ConfirmSponsorshipFail');
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
await sponsorCollection.methods.confirmCollectionSponsorship().send();
data = (await helper.rft.getData(collectionId))!;
@@ -135,7 +135,7 @@
itEth('Collection address exist', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
- const collectionHelpers = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelpers = helper.ethNativeContract.collectionHelpers(owner);
expect(await collectionHelpers
.methods.isCollectionExist(collectionAddressForNonexistentCollection).call())
@@ -147,7 +147,7 @@
.to.be.true;
// check collectionOwner:
- const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
+ const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'ft', owner, true);
const collectionOwner = await collectionEvm.methods.collectionOwner().call();
expect(helper.address.restoreCrossAccountFromBigInt(BigInt(collectionOwner.sub))).to.eq(helper.address.ethToSubstrate(owner, true));
});
@@ -167,7 +167,7 @@
itEth('(!negative test!) Create collection (bad lengths)', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
{
const MAX_NAME_LENGTH = 64;
const collectionName = 'A'.repeat(MAX_NAME_LENGTH + 1);
@@ -200,7 +200,7 @@
itEth('(!negative test!) Create collection (no funds)', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
await expect(collectionHelper.methods
.createRFTCollection('Peasantry', 'absolutely anything', 'TWIW')
.call({value: Number(1n * nominal)})).to.be.rejectedWith('Sent amount not equals to collection creation price (2000000000000000000)');
@@ -211,7 +211,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const peasant = helper.eth.createAccount();
const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
- const peasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', peasant, true);
+ const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant, true);
const EXPECTED_ERROR = 'NoPermission';
{
const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -219,7 +219,7 @@
.setCollectionSponsor(sponsor)
.call()).to.be.rejectedWith(EXPECTED_ERROR);
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor, true);
await expect(sponsorCollection.methods
.confirmCollectionSponsorship()
.call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -235,7 +235,7 @@
const owner = await helper.eth.createAccountWithBalance(donor);
const peasant = helper.eth.createAccount();
const {collectionAddress} = await helper.eth.createRFTCollection(owner, 'Transgressed', 'absolutely anything', 'YVNE');
- const peasantCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);
+ const peasantCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', peasant);
const EXPECTED_ERROR = 'NoPermission';
{
const sponsor = await helper.eth.createAccountWithBalance(donor);
@@ -244,7 +244,7 @@
.setCollectionSponsorCross(sponsorCross)
.call()).to.be.rejectedWith(EXPECTED_ERROR);
- const sponsorCollection = await helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
+ const sponsorCollection = helper.ethNativeContract.collection(collectionAddress, 'rft', sponsor);
await expect(sponsorCollection.methods
.confirmCollectionSponsorship()
.call()).to.be.rejectedWith('ConfirmSponsorshipFail');
@@ -259,7 +259,7 @@
itEth('destroyCollection', async ({helper}) => {
const owner = await helper.eth.createAccountWithBalance(donor);
const {collectionAddress, collectionId} = await helper.eth.createRFTCollection(owner, 'Limits', 'absolutely anything', 'OLF');
- const collectionHelper = await helper.ethNativeContract.collectionHelpers(owner);
+ const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
await expect(collectionHelper.methods
.destroyCollection(collectionAddress)
tests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -207,7 +207,7 @@
}
private async createTransaction() {
- const collectionHelper = await this.helper.ethNativeContract.collectionHelpers(this.signer);
+ const collectionHelper = this.helper.ethNativeContract.collectionHelpers(this.signer);
let collectionMode;
switch (this.data.collectionMode) {
case 'nft': collectionMode = CollectionMode.Nonfungible; break;
tests/src/util/index.tsdiffbeforeafterboth--- a/tests/src/util/index.ts
+++ b/tests/src/util/index.ts
@@ -15,6 +15,7 @@
import {dirname} from 'path';
import {fileURLToPath} from 'url';
+chai.config.truncateThreshold = 0;
chai.use(chaiAsPromised);
chai.use(chaiSubset);
export const expect = chai.expect;
tests/src/util/playgrounds/unique.dev.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.dev.ts
+++ b/tests/src/util/playgrounds/unique.dev.ts
@@ -41,7 +41,9 @@
for(const arg of args) {
if(typeof arg !== 'string')
continue;
- if(arg.includes('1000:: Normal connection closure') || arg.includes('Not decorating unknown runtime apis:') || arg.includes('RPC methods not decorated:') || arg === 'Normal connection closure')
+ const skippedWarnings = ['1000:: Normal connection closure', 'Not decorating unknown runtime apis:', 'RPC methods not decorated:', 'Not decorating runtime apis'];
+ const needToSkip = skippedWarnings.reduce((a, b) => a || arg.includes(b), false);
+ if(needToSkip || arg === 'Normal connection closure')
return;
}
printer(...args);