123456import { ApiPromise, Keyring } from '@polkadot/api';7import type { AccountId, EventRecord } from '@polkadot/types/interfaces';8import { IKeyringPair } from '@polkadot/types/types';9import { evmToAddress } from '@polkadot/util-crypto';10import { BigNumber } from 'bignumber.js';11import BN from 'bn.js';12import chai from 'chai';13import chaiAsPromised from 'chai-as-promised';14import { alicesPublicKey } from '../accounts';15import privateKey from '../substrate/privateKey';16import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from '../substrate/substrate-api';17import { ICollectionInterface } from '../types';18import { hexToStr, strToUTF16, utf16ToStr } from './util';1920chai.use(chaiAsPromised);21const expect = chai.expect;2223export type CrossAccountId = {24 substrate: string,25} | {26 ethereum: string,27};28export function normalizeAccountId(input: string | CrossAccountId | IKeyringPair): CrossAccountId {29 if (typeof input === 'string')30 return { substrate: input };31 if ('address' in input) {32 return { substrate: input.address };33 }34 if ('ethereum' in input) {35 input.ethereum = input.ethereum.toLowerCase();36 }37 return input;38}39export function toSubstrateAddress(input: string | CrossAccountId | IKeyringPair): string {40 input = normalizeAccountId(input);41 if ('substrate' in input) {42 return input.substrate;43 } else {44 return evmToAddress(input.ethereum);45 }46}4748export const U128_MAX = (1n << 128n) - 1n;4950type GenericResult = {51 success: boolean,52};5354interface CreateCollectionResult {55 success: boolean;56 collectionId: number;57}5859interface CreateItemResult {60 success: boolean;61 collectionId: number;62 itemId: number;63 recipient?: CrossAccountId;64}6566interface TransferResult {67 success: boolean;68 collectionId: number;69 itemId: number;70 sender?: CrossAccountId;71 recipient?: CrossAccountId;72 value: bigint;73}7475interface IReFungibleOwner {76 Fraction: BN;77 Owner: number[];78}7980interface ITokenDataType {81 Owner: number[];82 ConstData: number[];83 VariableData: number[];84}8586interface IGetMessage {87 checkMsgNftMethod: string;88 checkMsgTrsMethod: string;89 checkMsgSysMethod: string;90}9192export interface IReFungibleTokenDataType {93 Owner: IReFungibleOwner[];94 ConstData: number[];95 VariableData: number[];96}9798export function nftEventMessage(events: EventRecord[]): IGetMessage {99 let checkMsgNftMethod = '';100 let checkMsgTrsMethod = '';101 let checkMsgSysMethod = '';102 events.forEach(({ event: { method, section } }) => {103 if (section === 'nft') {104 checkMsgNftMethod = method;105 } else if (section === 'treasury') {106 checkMsgTrsMethod = method;107 } else if (section === 'system') {108 checkMsgSysMethod = method;109 } else { return null; }110 });111 const result: IGetMessage = {112 checkMsgNftMethod,113 checkMsgTrsMethod,114 checkMsgSysMethod,115 };116 return result;117}118119export function getGenericResult(events: EventRecord[]): GenericResult {120 const result: GenericResult = {121 success: false,122 };123 events.forEach(({ event: { method } }) => {124 125 if (method === 'ExtrinsicSuccess') {126 result.success = true;127 }128 });129 return result;130}131132133134export function getCreateCollectionResult(events: EventRecord[]): CreateCollectionResult {135 let success = false;136 let collectionId = 0;137 events.forEach(({ event: { data, method, section } }) => {138 139 if (method == 'ExtrinsicSuccess') {140 success = true;141 } else if ((section == 'nft') && (method == 'CollectionCreated')) {142 collectionId = parseInt(data[0].toString());143 }144 });145 const result: CreateCollectionResult = {146 success,147 collectionId,148 };149 return result;150}151152export function getCreateItemResult(events: EventRecord[]): CreateItemResult {153 let success = false;154 let collectionId = 0;155 let itemId = 0;156 let recipient;157 events.forEach(({ event: { data, method, section } }) => {158 159 if (method == 'ExtrinsicSuccess') {160 success = true;161 } else if ((section == 'nft') && (method == 'ItemCreated')) {162 collectionId = parseInt(data[0].toString());163 itemId = parseInt(data[1].toString());164 recipient = data[2].toJSON();165 }166 });167 const result: CreateItemResult = {168 success,169 collectionId,170 itemId,171 recipient,172 };173 return result;174}175176export function getTransferResult(events: EventRecord[]): TransferResult {177 const result: TransferResult = {178 success: false,179 collectionId: 0,180 itemId: 0,181 value: 0n,182 };183184 events.forEach(({ event: { data, method, section } }) => {185 if (method === 'ExtrinsicSuccess') {186 result.success = true;187 } else if (section === 'nft' && method === 'Transfer') {188 result.collectionId = +data[0].toString();189 result.itemId = +data[1].toString();190 result.sender = data[2].toJSON() as CrossAccountId;191 result.recipient = data[3].toJSON() as CrossAccountId;192 result.value = BigInt(data[4].toString());193 }194 });195196 return result;197}198199interface Invalid {200 type: 'Invalid';201}202203interface Nft {204 type: 'NFT';205}206207interface Fungible {208 type: 'Fungible';209 decimalPoints: number;210}211212interface ReFungible {213 type: 'ReFungible';214}215216type CollectionMode = Nft | Fungible | ReFungible | Invalid;217218export type CreateCollectionParams = {219 mode: CollectionMode,220 name: string,221 description: string,222 tokenPrefix: string,223};224225const defaultCreateCollectionParams: CreateCollectionParams = {226 description: 'description',227 mode: { type: 'NFT' },228 name: 'name',229 tokenPrefix: 'prefix',230};231232export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {233 const { name, description, mode, tokenPrefix } = { ...defaultCreateCollectionParams, ...params };234235 let collectionId = 0;236 await usingApi(async (api) => {237 238 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);239240 241 const alicePrivateKey = privateKey('//Alice');242243 let modeprm = {};244 if (mode.type === 'NFT') {245 modeprm = { nft: null };246 } else if (mode.type === 'Fungible') {247 modeprm = { fungible: mode.decimalPoints };248 } else if (mode.type === 'ReFungible') {249 modeprm = { refungible: null };250 } else if (mode.type === 'Invalid') {251 modeprm = { invalid: null };252 }253254 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);255 const events = await submitTransactionAsync(alicePrivateKey, tx);256 const result = getCreateCollectionResult(events);257258 259 const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);260261 262 const collection: any = (await api.query.nft.collectionById(result.collectionId) as any).toJSON();263264 265 266 expect(result.success).to.be.true;267 expect(result.collectionId).to.be.equal(BcollectionCount);268 269 expect(collection).to.be.not.null;270 expect(BcollectionCount).to.be.equal(AcollectionCount + 1, 'Error: NFT collection NOT created.');271 expect(collection.Owner).to.be.equal(toSubstrateAddress(alicesPublicKey));272 expect(utf16ToStr(collection.Name)).to.be.equal(name);273 expect(utf16ToStr(collection.Description)).to.be.equal(description);274 expect(hexToStr(collection.TokenPrefix)).to.be.equal(tokenPrefix);275276 collectionId = result.collectionId;277 });278279 return collectionId;280}281282export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {283 const { name, description, mode, tokenPrefix } = { ...defaultCreateCollectionParams, ...params };284285 let modeprm = {};286 if (mode.type === 'NFT') {287 modeprm = { nft: null };288 } else if (mode.type === 'Fungible') {289 modeprm = { fungible: mode.decimalPoints };290 } else if (mode.type === 'ReFungible') {291 modeprm = { refungible: null };292 } else if (mode.type === 'Invalid') {293 modeprm = { invalid: null };294 }295296 await usingApi(async (api) => {297 298 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());299300 301 const alicePrivateKey = privateKey('//Alice');302 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);303 const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;304 const result = getCreateCollectionResult(events);305306 307 const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());308309 310 311 expect(result.success).to.be.false;312 expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');313 });314}315316export async function findUnusedAddress(api: ApiPromise, seedAddition = ''): Promise<IKeyringPair> {317 let bal = new BigNumber(0);318 let unused;319 do {320 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;321 const keyring = new Keyring({ type: 'sr25519' });322 unused = keyring.addFromUri(`//${randomSeed}`);323 bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());324 } while (bal.toFixed() != '0');325 return unused;326}327328export async function getAllowance(collectionId: number, tokenId: number, owner: string, approved: string) {329 return await usingApi(async (api) => {330 const bn = await api.query.nft.allowances(collectionId, [tokenId, owner, approved]) as unknown as BN;331 return BigInt(bn.toString());332 });333}334335export function findUnusedAddresses(api: ApiPromise, amount: number): Promise<IKeyringPair[]> {336 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(api, '_' + Date.now())));337}338339export async function findNotExistingCollection(api: ApiPromise): Promise<number> {340 const totalNumber = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10) as unknown as number;341 const newCollection: number = totalNumber + 1;342 return newCollection;343}344345function getDestroyResult(events: EventRecord[]): boolean {346 let success = false;347 events.forEach(({ event: { method } }) => {348 if (method == 'ExtrinsicSuccess') {349 success = true;350 }351 });352 return success;353}354355export async function destroyCollectionExpectFailure(collectionId: number, senderSeed = '//Alice') {356 await usingApi(async (api) => {357 358 const alicePrivateKey = privateKey(senderSeed);359 const tx = api.tx.nft.destroyCollection(collectionId);360 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;361 });362}363364export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed = '//Alice') {365 await usingApi(async (api) => {366 367 const alicePrivateKey = privateKey(senderSeed);368 const tx = api.tx.nft.destroyCollection(collectionId);369 const events = await submitTransactionAsync(alicePrivateKey, tx);370 const result = getDestroyResult(events);371372 373 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();374375 376 expect(result).to.be.true;377 expect(collection).to.be.null;378 });379}380381export async function queryCollectionLimits(collectionId: number) {382 return await usingApi(async (api) => {383 return ((await api.query.nft.collectionById(collectionId)).toJSON() as any).Limits;384 });385}386387export async function setCollectionLimitsExpectSuccess(sender: IKeyringPair, collectionId: number, limits: any) {388 await usingApi(async (api) => {389 const oldLimits = await queryCollectionLimits(collectionId);390 const newLimits = { ...oldLimits as any, ...limits };391 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);392 const events = await submitTransactionAsync(sender, tx);393 const result = getGenericResult(events);394395 expect(result.success).to.be.true;396 });397}398399export async function setCollectionLimitsExpectFailure(sender: IKeyringPair, collectionId: number, limits: any) {400 await usingApi(async (api) => {401 const oldLimits = await queryCollectionLimits(collectionId);402 const newLimits = { ...oldLimits as any, ...limits };403 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);404 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;405 const result = getGenericResult(events);406407 expect(result.success).to.be.false;408 });409}410411export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {412 await usingApi(async (api) => {413414 415 const alicePrivateKey = privateKey('//Alice');416 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);417 const events = await submitTransactionAsync(alicePrivateKey, tx);418 const result = getGenericResult(events);419420 421 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();422423 424 expect(result.success).to.be.true;425 expect(collection.Sponsorship).to.deep.equal({426 unconfirmed: sponsor,427 });428 });429}430431export async function removeCollectionSponsorExpectSuccess(collectionId: number) {432 await usingApi(async (api) => {433434 435 const alicePrivateKey = privateKey('//Alice');436 const tx = api.tx.nft.removeCollectionSponsor(collectionId);437 const events = await submitTransactionAsync(alicePrivateKey, tx);438 const result = getGenericResult(events);439440 441 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();442443 444 expect(result.success).to.be.true;445 expect(collection.Sponsorship).to.be.deep.equal({ disabled: null });446 });447}448449export async function removeCollectionSponsorExpectFailure(collectionId: number) {450 await usingApi(async (api) => {451452 453 const alicePrivateKey = privateKey('//Alice');454 const tx = api.tx.nft.removeCollectionSponsor(collectionId);455 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;456 });457}458459export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed = '//Alice') {460 await usingApi(async (api) => {461462 463 const alicePrivateKey = privateKey(senderSeed);464 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);465 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;466 });467}468469export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed = '//Alice') {470 await usingApi(async (api) => {471472 473 const sender = privateKey(senderSeed);474 const tx = api.tx.nft.confirmSponsorship(collectionId);475 const events = await submitTransactionAsync(sender, tx);476 const result = getGenericResult(events);477478 479 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();480481 482 expect(result.success).to.be.true;483 expect(collection.Sponsorship).to.be.deep.equal({484 confirmed: sender.address,485 });486 });487}488489490export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed = '//Alice') {491 await usingApi(async (api) => {492493 494 const sender = privateKey(senderSeed);495 const tx = api.tx.nft.confirmSponsorship(collectionId);496 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;497 });498}499500export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {501 await usingApi(async (api) => {502 const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);503 const events = await submitTransactionAsync(sender, tx);504 const result = getGenericResult(events);505506 expect(result.success).to.be.true;507 });508}509510export async function enableContractSponsoringExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {511 await usingApi(async (api) => {512 const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);513 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;514 const result = getGenericResult(events);515516 expect(result.success).to.be.false;517 });518}519520export async function setContractSponsoringRateLimitExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {521 await usingApi(async (api) => {522 const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);523 const events = await submitTransactionAsync(sender, tx);524 const result = getGenericResult(events);525526 expect(result.success).to.be.true;527 });528}529530export async function setContractSponsoringRateLimitExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {531 await usingApi(async (api) => {532 const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);533 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;534 const result = getGenericResult(events);535536 expect(result.success).to.be.false;537 });538}539540export async function toggleContractWhitelistExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string) {541 await usingApi(async (api) => {542 const tx = api.tx.nft.toggleContractWhiteList(contractAddress, true);543 const events = await submitTransactionAsync(sender, tx);544 const result = getGenericResult(events);545546 expect(result.success).to.be.true;547 });548}549550export async function isWhitelistedInContract(contractAddress: AccountId | string, user: string) {551 let whitelisted = false;552 await usingApi(async (api) => {553 whitelisted = (await api.query.nft.contractWhiteList(contractAddress, user)).toJSON() as boolean;554 });555 return whitelisted;556}557558export async function addToContractWhiteListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {559 await usingApi(async (api) => {560 const tx = api.tx.nft.addToContractWhiteList(contractAddress.toString(), user.toString());561 const events = await submitTransactionAsync(sender, tx);562 const result = getGenericResult(events);563564 expect(result.success).to.be.true;565 });566}567568export async function removeFromContractWhiteListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {569 await usingApi(async (api) => {570 const tx = api.tx.nft.removeFromContractWhiteList(contractAddress.toString(), user.toString());571 const events = await submitTransactionAsync(sender, tx);572 const result = getGenericResult(events);573574 expect(result.success).to.be.true;575 });576}577578export async function removeFromContractWhiteListExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {579 await usingApi(async (api) => {580 const tx = api.tx.nft.removeFromContractWhiteList(contractAddress.toString(), user.toString());581 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;582 const result = getGenericResult(events);583584 expect(result.success).to.be.false;585 });586}587588export async function setVariableMetaDataExpectSuccess(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {589 await usingApi(async (api) => {590 const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));591 const events = await submitTransactionAsync(sender, tx);592 const result = getGenericResult(events);593594 expect(result.success).to.be.true;595 });596}597598export async function setVariableMetaDataExpectFailure(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {599 await usingApi(async (api) => {600 const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));601 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;602 });603}604605export async function setOffchainSchemaExpectSuccess(sender: IKeyringPair, collectionId: number, data: number[]) {606 await usingApi(async (api) => {607 const tx = api.tx.nft.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));608 const events = await submitTransactionAsync(sender, tx);609 const result = getGenericResult(events);610611 expect(result.success).to.be.true;612 });613}614615export async function setOffchainSchemaExpectFailure(sender: IKeyringPair, collectionId: number, data: number[]) {616 await usingApi(async (api) => {617 const tx = api.tx.nft.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));618 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;619 });620}621622export interface CreateFungibleData {623 readonly Value: bigint;624}625626export interface CreateReFungibleData { }627export interface CreateNftData { }628629export type CreateItemData = {630 NFT: CreateNftData;631} | {632 Fungible: CreateFungibleData;633} | {634 ReFungible: CreateReFungibleData;635};636637export async function burnItemExpectSuccess(owner: IKeyringPair, collectionId: number, tokenId: number, value = 0) {638 await usingApi(async (api) => {639 const tx = api.tx.nft.burnItem(collectionId, tokenId, value);640 const events = await submitTransactionAsync(owner, tx);641 const result = getGenericResult(events);642 643 const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();644 645 646 expect(result.success).to.be.true;647 648 expect(item).to.be.null;649 });650}651652export async function653approveExpectSuccess(654 collectionId: number,655 tokenId: number, owner: IKeyringPair, approved: IKeyringPair | CrossAccountId | string, amount: number | bigint = 1,656) {657 await usingApi(async (api: ApiPromise) => {658 approved = normalizeAccountId(approved);659 const allowanceBefore =660 await api.query.nft.allowances(collectionId, [tokenId, owner.address, toSubstrateAddress(approved)]) as unknown as BN;661 const approveNftTx = api.tx.nft.approve(approved, collectionId, tokenId, amount);662 const events = await submitTransactionAsync(owner, approveNftTx);663 const result = getCreateItemResult(events);664 665 expect(result.success).to.be.true;666 const allowanceAfter =667 await api.query.nft.allowances(collectionId, [tokenId, owner.address, toSubstrateAddress(approved)]) as unknown as BN;668 expect(allowanceAfter.sub(allowanceBefore).toString()).to.be.equal(amount.toString());669 });670}671672export async function673transferFromExpectSuccess(674 collectionId: number,675 tokenId: number,676 accountApproved: IKeyringPair,677 accountFrom: IKeyringPair | CrossAccountId,678 accountTo: IKeyringPair | CrossAccountId,679 value: number | bigint = 1,680 type = 'NFT',681) {682 await usingApi(async (api: ApiPromise) => {683 const to = normalizeAccountId(accountTo);684 let balanceBefore = new BN(0);685 if (type === 'Fungible') {686 balanceBefore = await api.query.nft.balance(collectionId, toSubstrateAddress(to)) as unknown as BN;687 }688 const transferFromTx = api.tx.nft.transferFrom(normalizeAccountId(accountFrom), to, collectionId, tokenId, value);689 const events = await submitTransactionAsync(accountApproved, transferFromTx);690 const result = getCreateItemResult(events);691 692 expect(result.success).to.be.true;693 if (type === 'NFT') {694 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId) as any).toJSON() as ITokenDataType;695 expect(nftItemData.Owner).to.be.deep.equal(to);696 }697 if (type === 'Fungible') {698 const balanceAfter = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to)) as any).Value as unknown as BN;699 expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(value.toString());700 }701 if (type === 'ReFungible') {702 const nftItemData =703 (await api.query.nft.reFungibleItemList(collectionId, tokenId) as any).toJSON() as IReFungibleTokenDataType;704 expect(nftItemData.Owner[0].Owner).to.be.deep.equal(normalizeAccountId(to));705 expect(nftItemData.Owner[0].Fraction).to.be.equal(value);706 }707 });708}709710export async function711transferFromExpectFail(712 collectionId: number,713 tokenId: number,714 accountApproved: IKeyringPair,715 accountFrom: IKeyringPair,716 accountTo: IKeyringPair,717 value: number | bigint = 1,718) {719 await usingApi(async (api: ApiPromise) => {720 const transferFromTx = api.tx.nft.transferFrom(normalizeAccountId(accountFrom.address), normalizeAccountId(accountTo.address), collectionId, tokenId, value);721 const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;722 const result = getCreateCollectionResult(events);723 724 expect(result.success).to.be.false;725 });726}727728729async function getBlockNumber(api: ApiPromise): Promise<number> {730 return new Promise<number>(async (resolve) => {731 const unsubscribe = await api.rpc.chain.subscribeNewHeads((head) => {732 unsubscribe();733 resolve(head.number.toNumber());734 });735 });736}737738export async function739scheduleTransferExpectSuccess(740 collectionId: number,741 tokenId: number,742 sender: IKeyringPair,743 recipient: IKeyringPair,744 value: number | bigint = 1,745) {746 await usingApi(async (api: ApiPromise) => {747 const blockNumber: number | undefined = await getBlockNumber(api);748 const expectedBlockNumber = blockNumber + 2;749750 expect(blockNumber).to.be.greaterThan(0);751 const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value); 752 const scheduleTx = await api.tx.scheduler.schedule(expectedBlockNumber, null, 0, transferTx);753754 await submitTransactionAsync(sender, scheduleTx);755756 const recipientBalanceBefore = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());757758 const nftItemDataBefore = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;759 expect(nftItemDataBefore.Owner.toString()).to.be.equal(sender.address);760761 762 await new Promise(resolve => setTimeout(resolve, 6000 * 2));763764 const recipientBalanceAfter = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());765766 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;767 expect(nftItemData.Owner.toString()).to.be.equal(recipient.address);768 expect(recipientBalanceAfter.toNumber()).to.be.equal(recipientBalanceBefore.toNumber());769 });770}771772773export async function774transferExpectSuccess(775 collectionId: number,776 tokenId: number,777 sender: IKeyringPair,778 recipient: IKeyringPair | CrossAccountId,779 value: number | bigint = 1,780 type = 'NFT',781) {782 await usingApi(async (api: ApiPromise) => {783 const to = normalizeAccountId(recipient);784785 let balanceBefore = new BN(0);786 if (type === 'Fungible') {787 balanceBefore = await api.query.nft.balance(collectionId, toSubstrateAddress(to)) as unknown as BN;788 }789 const transferTx = api.tx.nft.transfer(to, collectionId, tokenId, value);790 const events = await submitTransactionAsync(sender, transferTx);791 const result = getTransferResult(events);792 793 expect(result.success).to.be.true;794 expect(result.collectionId).to.be.equal(collectionId);795 expect(result.itemId).to.be.equal(tokenId);796 expect(result.sender).to.be.deep.equal(normalizeAccountId(sender.address));797 expect(result.recipient).to.be.deep.equal(to);798 expect(result.value.toString()).to.be.equal(value.toString());799 if (type === 'NFT') {800 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;801 expect(nftItemData.Owner).to.be.deep.equal(to);802 }803 if (type === 'Fungible') {804 const balanceAfter = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to)) as any).Value as unknown as BN;805 expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(value.toString());806 }807 if (type === 'ReFungible') {808 const nftItemData =809 (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON() as unknown as IReFungibleTokenDataType;810 expect(nftItemData.Owner[0].Owner).to.be.deep.equal(to);811 expect(nftItemData.Owner[0].Fraction.toString()).to.be.equal(value.toString());812 }813 });814}815816export async function817transferExpectFail(818 collectionId: number,819 tokenId: number,820 sender: IKeyringPair,821 recipient: IKeyringPair,822 value: number | bigint = 1,823) {824 await usingApi(async (api: ApiPromise) => {825 const transferTx = api.tx.nft.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);826 const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;827 if (events && Array.isArray(events)) {828 const result = getCreateCollectionResult(events);829 830 expect(result.success).to.be.false;831 }832 });833}834835export async function836approveExpectFail(837 collectionId: number,838 tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number | bigint = 1,839) {840 await usingApi(async (api: ApiPromise) => {841 const approveNftTx = api.tx.nft.approve(normalizeAccountId(approved.address), collectionId, tokenId, amount);842 const events = await expect(submitTransactionExpectFailAsync(owner, approveNftTx)).to.be.rejected;843 const result = getCreateCollectionResult(events);844 845 expect(result.success).to.be.false;846 });847}848849export async function getFungibleBalance(850 collectionId: number,851 owner: string,852) {853 return await usingApi(async (api) => {854 const response = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON() as unknown as { Value: string };855 return BigInt(response.Value);856 });857}858859export async function createFungibleItemExpectSuccess(860 sender: IKeyringPair,861 collectionId: number,862 data: CreateFungibleData,863 owner: CrossAccountId | string = sender.address,864) {865 return await usingApi(async (api) => {866 const tx = api.tx.nft.createItem(collectionId, normalizeAccountId(owner), { Fungible: data });867868 const events = await submitTransactionAsync(sender, tx);869 const result = getCreateItemResult(events);870871 expect(result.success).to.be.true;872 return result.itemId;873 });874}875876export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: CrossAccountId | string = sender.address) {877 let newItemId = 0;878 await usingApi(async (api) => {879 const to = normalizeAccountId(owner);880 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);881 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to))).toJSON();882 const AItemBalance = new BigNumber(Aitem.Value);883884 let tx;885 if (createMode === 'Fungible') {886 const createData = { fungible: { value: 10 } };887 tx = api.tx.nft.createItem(collectionId, to, createData);888 } else if (createMode === 'ReFungible') {889 const createData = { refungible: { const_data: [], variable_data: [], pieces: 100 } };890 tx = api.tx.nft.createItem(collectionId, to, createData);891 } else {892 tx = api.tx.nft.createItem(collectionId, to, createMode);893 }894895 const events = await submitTransactionAsync(sender, tx);896 const result = getCreateItemResult(events);897898 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);899 const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to))).toJSON();900 const BItemBalance = new BigNumber(Bitem.Value);901902 903 904 expect(result.success).to.be.true;905 if (createMode === 'Fungible') {906 expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);907 } else {908 expect(BItemCount).to.be.equal(AItemCount + 1);909 }910 expect(collectionId).to.be.equal(result.collectionId);911 expect(BItemCount.toString()).to.be.equal(result.itemId.toString());912 expect(to).to.be.deep.equal(result.recipient);913 newItemId = result.itemId;914 });915 return newItemId;916}917918export async function createItemExpectFailure(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = sender.address) {919 await usingApi(async (api) => {920 const tx = api.tx.nft.createItem(collectionId, normalizeAccountId(owner), createMode);921 922 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;923 const result = getCreateItemResult(events);924925 expect(result.success).to.be.false;926 });927}928929export async function setPublicAccessModeExpectSuccess(930 sender: IKeyringPair, collectionId: number,931 accessMode: 'Normal' | 'WhiteList',932) {933 await usingApi(async (api) => {934935 936 const tx = api.tx.nft.setPublicAccessMode(collectionId, accessMode);937 const events = await submitTransactionAsync(sender, tx);938 const result = getGenericResult(events);939940 941 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();942943 944 945 expect(result.success).to.be.true;946 expect(collection.Access).to.be.equal(accessMode);947 });948}949950export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {951 await setPublicAccessModeExpectSuccess(sender, collectionId, 'WhiteList');952}953954export async function disableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {955 await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal');956}957958export async function setMintPermissionExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {959 await usingApi(async (api) => {960961 962 const tx = api.tx.nft.setMintPermission(collectionId, enabled);963 const events = await submitTransactionAsync(sender, tx);964 const result = getGenericResult(events);965966 967 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();968969 970 971 expect(result.success).to.be.true;972 expect(collection.MintMode).to.be.equal(enabled);973 });974}975976export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {977 await setMintPermissionExpectSuccess(sender, collectionId, true);978}979980export async function setMintPermissionExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {981 await usingApi(async (api) => {982 983 const tx = api.tx.nft.setMintPermission(collectionId, enabled);984 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;985 const result = getCreateCollectionResult(events);986 987 expect(result.success).to.be.false;988 });989}990991export async function isWhitelisted(collectionId: number, address: string) {992 let whitelisted = false;993 await usingApi(async (api) => {994 whitelisted = (await api.query.nft.whiteList(collectionId, address)).toJSON() as unknown as boolean;995 });996 return whitelisted;997}998999export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1000 await usingApi(async (api) => {10011002 const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();10031004 1005 const tx = api.tx.nft.addToWhiteList(collectionId, normalizeAccountId(address));1006 const events = await submitTransactionAsync(sender, tx);1007 const result = getGenericResult(events);10081009 const whiteListedAfter = (await api.query.nft.whiteList(collectionId, address)).toJSON();10101011 1012 1013 expect(result.success).to.be.true;1014 1015 expect(whiteListedBefore).to.be.false;1016 1017 expect(whiteListedAfter).to.be.true;1018 });1019}10201021export async function removeFromWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1022 await usingApi(async (api) => {1023 1024 const tx = api.tx.nft.removeFromWhiteList(collectionId, normalizeAccountId(address));1025 const events = await submitTransactionAsync(sender, tx);1026 const result = getGenericResult(events);10271028 1029 1030 expect(result.success).to.be.true;1031 });1032}10331034export async function removeFromWhiteListExpectFailure(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1035 await usingApi(async (api) => {1036 1037 const tx = api.tx.nft.removeFromWhiteList(collectionId, normalizeAccountId(address));1038 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1039 const result = getGenericResult(events);10401041 1042 1043 expect(result.success).to.be.false;1044 });1045}10461047export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)1048 : Promise<ICollectionInterface | null> => {1049 return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;1050};10511052export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {1053 1054 return (await api.query.nft.createdCollectionCount() as unknown as BN).toNumber();1055};10561057export async function queryCollectionExpectSuccess(collectionId: number): Promise<ICollectionInterface> {1058 return await usingApi(async (api) => {1059 return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;1060 });1061}