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 | AccountId | 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 return input;37 }38 if ('substrate' in input) {39 return input;40 }4142 43 return {substrate: input.toString()};44}45export function toSubstrateAddress(input: string | CrossAccountId | IKeyringPair): string {46 input = normalizeAccountId(input);47 if ('substrate' in input) {48 return input.substrate;49 } else {50 return evmToAddress(input.ethereum);51 }52}5354export const U128_MAX = (1n << 128n) - 1n;5556const MICROUNIQUE = 1_000_000_000n;57const MILLIUNIQUE = 1_000n * MICROUNIQUE;58const CENTIUNIQUE = 10n * MILLIUNIQUE;59export const UNIQUE = 100n * CENTIUNIQUE;6061type GenericResult = {62 success: boolean,63};6465interface CreateCollectionResult {66 success: boolean;67 collectionId: number;68}6970interface CreateItemResult {71 success: boolean;72 collectionId: number;73 itemId: number;74 recipient?: CrossAccountId;75}7677interface TransferResult {78 success: boolean;79 collectionId: number;80 itemId: number;81 sender?: CrossAccountId;82 recipient?: CrossAccountId;83 value: bigint;84}8586interface IReFungibleOwner {87 Fraction: BN;88 Owner: number[];89}9091interface ITokenDataType {92 Owner: IKeyringPair;93 ConstData: number[];94 VariableData: number[];95}9697interface IGetMessage {98 checkMsgNftMethod: string;99 checkMsgTrsMethod: string;100 checkMsgSysMethod: string;101}102103export interface IReFungibleTokenDataType {104 Owner: IReFungibleOwner[];105 ConstData: number[];106 VariableData: number[];107}108109export function nftEventMessage(events: EventRecord[]): IGetMessage {110 let checkMsgNftMethod = '';111 let checkMsgTrsMethod = '';112 let checkMsgSysMethod = '';113 events.forEach(({ event: { method, section } }) => {114 if (section === 'nft') {115 checkMsgNftMethod = method;116 } else if (section === 'treasury') {117 checkMsgTrsMethod = method;118 } else if (section === 'system') {119 checkMsgSysMethod = method;120 } else { return null; }121 });122 const result: IGetMessage = {123 checkMsgNftMethod,124 checkMsgTrsMethod,125 checkMsgSysMethod,126 };127 return result;128}129130export function getGenericResult(events: EventRecord[]): GenericResult {131 const result: GenericResult = {132 success: false,133 };134 events.forEach(({ event: { method } }) => {135 136 if (method === 'ExtrinsicSuccess') {137 result.success = true;138 }139 });140 return result;141}142143144145export function getCreateCollectionResult(events: EventRecord[]): CreateCollectionResult {146 let success = false;147 let collectionId = 0;148 events.forEach(({ event: { data, method, section } }) => {149 150 if (method == 'ExtrinsicSuccess') {151 success = true;152 } else if ((section == 'nft') && (method == 'CollectionCreated')) {153 collectionId = parseInt(data[0].toString());154 }155 });156 const result: CreateCollectionResult = {157 success,158 collectionId,159 };160 return result;161}162163export function getCreateItemResult(events: EventRecord[]): CreateItemResult {164 let success = false;165 let collectionId = 0;166 let itemId = 0;167 let recipient;168 events.forEach(({ event: { data, method, section } }) => {169 170 if (method == 'ExtrinsicSuccess') {171 success = true;172 } else if ((section == 'nft') && (method == 'ItemCreated')) {173 collectionId = parseInt(data[0].toString());174 itemId = parseInt(data[1].toString());175 recipient = data[2].toJSON();176 }177 });178 const result: CreateItemResult = {179 success,180 collectionId,181 itemId,182 recipient,183 };184 return result;185}186187export function getTransferResult(events: EventRecord[]): TransferResult {188 const result: TransferResult = {189 success: false,190 collectionId: 0,191 itemId: 0,192 value: 0n,193 };194195 events.forEach(({ event: { data, method, section } }) => {196 if (method === 'ExtrinsicSuccess') {197 result.success = true;198 } else if (section === 'nft' && method === 'Transfer') {199 result.collectionId = +data[0].toString();200 result.itemId = +data[1].toString();201 result.sender = data[2].toJSON() as CrossAccountId;202 result.recipient = data[3].toJSON() as CrossAccountId;203 result.value = BigInt(data[4].toString());204 }205 });206207 return result;208}209210interface Invalid {211 type: 'Invalid';212}213214interface Nft {215 type: 'NFT';216}217218interface Fungible {219 type: 'Fungible';220 decimalPoints: number;221}222223interface ReFungible {224 type: 'ReFungible';225}226227type CollectionMode = Nft | Fungible | ReFungible | Invalid;228229export type CreateCollectionParams = {230 mode: CollectionMode,231 name: string,232 description: string,233 tokenPrefix: string,234};235236const defaultCreateCollectionParams: CreateCollectionParams = {237 description: 'description',238 mode: { type: 'NFT' },239 name: 'name',240 tokenPrefix: 'prefix',241};242243export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {244 const { name, description, mode, tokenPrefix } = { ...defaultCreateCollectionParams, ...params };245246 let collectionId = 0;247 await usingApi(async (api) => {248 249 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);250251 252 const alicePrivateKey = privateKey('//Alice');253254 let modeprm = {};255 if (mode.type === 'NFT') {256 modeprm = { nft: null };257 } else if (mode.type === 'Fungible') {258 modeprm = { fungible: mode.decimalPoints };259 } else if (mode.type === 'ReFungible') {260 modeprm = { refungible: null };261 } else if (mode.type === 'Invalid') {262 modeprm = { invalid: null };263 }264265 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);266 const events = await submitTransactionAsync(alicePrivateKey, tx);267 const result = getCreateCollectionResult(events);268269 270 const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);271272 273 const collection: any = (await api.query.nft.collectionById(result.collectionId) as any).toJSON();274275 276 277 expect(result.success).to.be.true;278 expect(result.collectionId).to.be.equal(BcollectionCount);279 280 expect(collection).to.be.not.null;281 expect(BcollectionCount).to.be.equal(AcollectionCount + 1, 'Error: NFT collection NOT created.');282 expect(collection.Owner).to.be.equal(toSubstrateAddress(alicesPublicKey));283 expect(utf16ToStr(collection.Name)).to.be.equal(name);284 expect(utf16ToStr(collection.Description)).to.be.equal(description);285 expect(hexToStr(collection.TokenPrefix)).to.be.equal(tokenPrefix);286287 collectionId = result.collectionId;288 });289290 return collectionId;291}292293export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {294 const { name, description, mode, tokenPrefix } = { ...defaultCreateCollectionParams, ...params };295296 let modeprm = {};297 if (mode.type === 'NFT') {298 modeprm = { nft: null };299 } else if (mode.type === 'Fungible') {300 modeprm = { fungible: mode.decimalPoints };301 } else if (mode.type === 'ReFungible') {302 modeprm = { refungible: null };303 } else if (mode.type === 'Invalid') {304 modeprm = { invalid: null };305 }306307 await usingApi(async (api) => {308 309 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());310311 312 const alicePrivateKey = privateKey('//Alice');313 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);314 const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;315 const result = getCreateCollectionResult(events);316317 318 const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());319320 321 322 expect(result.success).to.be.false;323 expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');324 });325}326327export async function findUnusedAddress(api: ApiPromise, seedAddition = ''): Promise<IKeyringPair> {328 let bal = new BigNumber(0);329 let unused;330 do {331 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;332 const keyring = new Keyring({ type: 'sr25519' });333 unused = keyring.addFromUri(`//${randomSeed}`);334 bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());335 } while (bal.toFixed() != '0');336 return unused;337}338339export async function getAllowance(collectionId: number, tokenId: number, owner: string, approved: string) {340 return await usingApi(async (api) => {341 const bn = await api.query.nft.allowances(collectionId, [tokenId, owner, approved]) as unknown as BN;342 return BigInt(bn.toString());343 });344}345346export function findUnusedAddresses(api: ApiPromise, amount: number): Promise<IKeyringPair[]> {347 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(api, '_' + Date.now())));348}349350export async function findNotExistingCollection(api: ApiPromise): Promise<number> {351 const totalNumber = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10) as unknown as number;352 const newCollection: number = totalNumber + 1;353 return newCollection;354}355356function getDestroyResult(events: EventRecord[]): boolean {357 let success = false;358 events.forEach(({ event: { method } }) => {359 if (method == 'ExtrinsicSuccess') {360 success = true;361 }362 });363 return success;364}365366export async function destroyCollectionExpectFailure(collectionId: number, senderSeed = '//Alice') {367 await usingApi(async (api) => {368 369 const alicePrivateKey = privateKey(senderSeed);370 const tx = api.tx.nft.destroyCollection(collectionId);371 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;372 });373}374375export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed = '//Alice') {376 await usingApi(async (api) => {377 378 const alicePrivateKey = privateKey(senderSeed);379 const tx = api.tx.nft.destroyCollection(collectionId);380 const events = await submitTransactionAsync(alicePrivateKey, tx);381 const result = getDestroyResult(events);382383 384 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();385386 387 expect(result).to.be.true;388 expect(collection).to.be.null;389 });390}391392export async function queryCollectionLimits(collectionId: number) {393 return await usingApi(async (api) => {394 return ((await api.query.nft.collectionById(collectionId)).toJSON() as any).Limits;395 });396}397398export async function setCollectionLimitsExpectSuccess(sender: IKeyringPair, collectionId: number, limits: any) {399 await usingApi(async (api) => {400 const oldLimits = await queryCollectionLimits(collectionId);401 const newLimits = { ...oldLimits as any, ...limits };402 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);403 const events = await submitTransactionAsync(sender, tx);404 const result = getGenericResult(events);405406 expect(result.success).to.be.true;407 });408}409410export async function setCollectionLimitsExpectFailure(sender: IKeyringPair, collectionId: number, limits: any) {411 await usingApi(async (api) => {412 const oldLimits = await queryCollectionLimits(collectionId);413 const newLimits = { ...oldLimits as any, ...limits };414 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);415 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;416 const result = getGenericResult(events);417418 expect(result.success).to.be.false;419 });420}421422export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string, sender = '//Alice') {423 await usingApi(async (api) => {424425 426 const senderPrivateKey = privateKey(sender);427 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);428 const events = await submitTransactionAsync(senderPrivateKey, tx);429 const result = getGenericResult(events);430431 432 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();433434 435 expect(result.success).to.be.true;436 expect(collection.Sponsorship).to.deep.equal({437 unconfirmed: sponsor,438 });439 });440}441442export async function removeCollectionSponsorExpectSuccess(collectionId: number, sender = '//Alice') {443 await usingApi(async (api) => {444445 446 const alicePrivateKey = privateKey(sender);447 const tx = api.tx.nft.removeCollectionSponsor(collectionId);448 const events = await submitTransactionAsync(alicePrivateKey, tx);449 const result = getGenericResult(events);450451 452 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();453454 455 expect(result.success).to.be.true;456 expect(collection.Sponsorship).to.be.deep.equal({ disabled: null });457 });458}459460export async function removeCollectionSponsorExpectFailure(collectionId: number) {461 await usingApi(async (api) => {462463 464 const alicePrivateKey = privateKey('//Alice');465 const tx = api.tx.nft.removeCollectionSponsor(collectionId);466 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;467 });468}469470export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed = '//Alice') {471 await usingApi(async (api) => {472473 474 const alicePrivateKey = privateKey(senderSeed);475 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);476 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;477 });478}479480export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed = '//Alice') {481 await usingApi(async (api) => {482483 484 const sender = privateKey(senderSeed);485 const tx = api.tx.nft.confirmSponsorship(collectionId);486 const events = await submitTransactionAsync(sender, tx);487 const result = getGenericResult(events);488489 490 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();491492 493 expect(result.success).to.be.true;494 expect(collection.Sponsorship).to.be.deep.equal({495 confirmed: sender.address,496 });497 });498}499500501export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed = '//Alice') {502 await usingApi(async (api) => {503504 505 const sender = privateKey(senderSeed);506 const tx = api.tx.nft.confirmSponsorship(collectionId);507 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;508 });509}510511export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {512 await usingApi(async (api) => {513 const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);514 const events = await submitTransactionAsync(sender, tx);515 const result = getGenericResult(events);516517 expect(result.success).to.be.true;518 });519}520521export async function enableContractSponsoringExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {522 await usingApi(async (api) => {523 const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);524 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;525 const result = getGenericResult(events);526527 expect(result.success).to.be.false;528 });529}530531export async function setTransferFlagExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {532533 await usingApi(async (api) => {534535 const tx = api.tx.nft.setTransfersEnabledFlag (collectionId, enabled);536 const events = await submitTransactionAsync(sender, tx);537 const result = getGenericResult(events);538539 expect(result.success).to.be.true;540 }); 541}542543export async function setTransferFlagExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {544545 await usingApi(async (api) => {546547 const tx = api.tx.nft.setTransfersEnabledFlag (collectionId, enabled);548 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;549 const result = getGenericResult(events);550551 expect(result.success).to.be.false;552 }); 553}554555export async function setContractSponsoringRateLimitExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {556 await usingApi(async (api) => {557 const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);558 const events = await submitTransactionAsync(sender, tx);559 const result = getGenericResult(events);560561 expect(result.success).to.be.true;562 });563}564565export async function setContractSponsoringRateLimitExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {566 await usingApi(async (api) => {567 const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);568 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;569 const result = getGenericResult(events);570571 expect(result.success).to.be.false;572 });573}574575export async function toggleContractWhitelistExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, value = true) {576 await usingApi(async (api) => {577 const tx = api.tx.nft.toggleContractWhiteList(contractAddress, value);578 const events = await submitTransactionAsync(sender, tx);579 const result = getGenericResult(events);580581 expect(result.success).to.be.true;582 });583}584585export async function isWhitelistedInContract(contractAddress: AccountId | string, user: string) {586 let whitelisted = false;587 await usingApi(async (api) => {588 whitelisted = (await api.query.nft.contractWhiteList(contractAddress, user)).toJSON() as boolean;589 });590 return whitelisted;591}592593export async function addToContractWhiteListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {594 await usingApi(async (api) => {595 const tx = api.tx.nft.addToContractWhiteList(contractAddress.toString(), user.toString());596 const events = await submitTransactionAsync(sender, tx);597 const result = getGenericResult(events);598599 expect(result.success).to.be.true;600 });601}602603export async function removeFromContractWhiteListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {604 await usingApi(async (api) => {605 const tx = api.tx.nft.removeFromContractWhiteList(contractAddress.toString(), user.toString());606 const events = await submitTransactionAsync(sender, tx);607 const result = getGenericResult(events);608609 expect(result.success).to.be.true;610 });611}612613export async function removeFromContractWhiteListExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {614 await usingApi(async (api) => {615 const tx = api.tx.nft.removeFromContractWhiteList(contractAddress.toString(), user.toString());616 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;617 const result = getGenericResult(events);618619 expect(result.success).to.be.false;620 });621}622623export async function setVariableMetaDataExpectSuccess(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {624 await usingApi(async (api) => {625 const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));626 const events = await submitTransactionAsync(sender, tx);627 const result = getGenericResult(events);628629 expect(result.success).to.be.true;630 });631}632633export async function setVariableMetaDataExpectFailure(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {634 await usingApi(async (api) => {635 const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));636 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;637 });638}639640export async function setOffchainSchemaExpectSuccess(sender: IKeyringPair, collectionId: number, data: number[]) {641 await usingApi(async (api) => {642 const tx = api.tx.nft.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));643 const events = await submitTransactionAsync(sender, tx);644 const result = getGenericResult(events);645646 expect(result.success).to.be.true;647 });648}649650export async function setOffchainSchemaExpectFailure(sender: IKeyringPair, collectionId: number, data: number[]) {651 await usingApi(async (api) => {652 const tx = api.tx.nft.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));653 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;654 });655}656657export interface CreateFungibleData {658 readonly Value: bigint;659}660661export interface CreateReFungibleData { }662export interface CreateNftData { }663664export type CreateItemData = {665 NFT: CreateNftData;666} | {667 Fungible: CreateFungibleData;668} | {669 ReFungible: CreateReFungibleData;670};671672export async function burnItemExpectSuccess(owner: IKeyringPair, collectionId: number, tokenId: number, value = 0) {673 await usingApi(async (api) => {674 const tx = api.tx.nft.burnItem(collectionId, tokenId, value);675 const events = await submitTransactionAsync(owner, tx);676 const result = getGenericResult(events);677 678 const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();679 680 681 expect(result.success).to.be.true;682 683 expect(item).to.be.null;684 });685}686687export async function688approveExpectSuccess(689 collectionId: number,690 tokenId: number, owner: IKeyringPair, approved: IKeyringPair | CrossAccountId | string, amount: number | bigint = 1,691) {692 await usingApi(async (api: ApiPromise) => {693 approved = normalizeAccountId(approved);694 const allowanceBefore =695 await api.query.nft.allowances(collectionId, [tokenId, owner.address, toSubstrateAddress(approved)]) as unknown as BN;696 const approveNftTx = api.tx.nft.approve(approved, collectionId, tokenId, amount);697 const events = await submitTransactionAsync(owner, approveNftTx);698 const result = getCreateItemResult(events);699 700 expect(result.success).to.be.true;701 const allowanceAfter =702 await api.query.nft.allowances(collectionId, [tokenId, owner.address, toSubstrateAddress(approved)]) as unknown as BN;703 expect(allowanceAfter.sub(allowanceBefore).toString()).to.be.equal(amount.toString());704 });705}706707export async function708transferFromExpectSuccess(709 collectionId: number,710 tokenId: number,711 accountApproved: IKeyringPair,712 accountFrom: IKeyringPair | CrossAccountId,713 accountTo: IKeyringPair | CrossAccountId,714 value: number | bigint = 1,715 type = 'NFT',716) {717 await usingApi(async (api: ApiPromise) => {718 const to = normalizeAccountId(accountTo);719 let balanceBefore = new BN(0);720 if (type === 'Fungible') {721 balanceBefore = await api.query.nft.balance(collectionId, toSubstrateAddress(to)) as unknown as BN;722 }723 const transferFromTx = api.tx.nft.transferFrom(normalizeAccountId(accountFrom), to, collectionId, tokenId, value);724 const events = await submitTransactionAsync(accountApproved, transferFromTx);725 const result = getCreateItemResult(events);726 727 expect(result.success).to.be.true;728 if (type === 'NFT') {729 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId) as any).toJSON() as ITokenDataType;730 expect(nftItemData.Owner).to.be.deep.equal(to);731 }732 if (type === 'Fungible') {733 const balanceAfter = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to)) as any).Value as unknown as BN;734 expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(value.toString());735 }736 if (type === 'ReFungible') {737 const nftItemData =738 (await api.query.nft.reFungibleItemList(collectionId, tokenId) as any).toJSON() as IReFungibleTokenDataType;739 expect(nftItemData.Owner[0].Owner).to.be.deep.equal(normalizeAccountId(to));740 expect(nftItemData.Owner[0].Fraction).to.be.equal(value);741 }742 });743}744745export async function746transferFromExpectFail(747 collectionId: number,748 tokenId: number,749 accountApproved: IKeyringPair,750 accountFrom: IKeyringPair,751 accountTo: IKeyringPair,752 value: number | bigint = 1,753) {754 await usingApi(async (api: ApiPromise) => {755 const transferFromTx = api.tx.nft.transferFrom(normalizeAccountId(accountFrom.address), normalizeAccountId(accountTo.address), collectionId, tokenId, value);756 const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;757 const result = getCreateCollectionResult(events);758 759 expect(result.success).to.be.false;760 });761}762763764async function getBlockNumber(api: ApiPromise): Promise<number> {765 return new Promise<number>(async (resolve) => {766 const unsubscribe = await api.rpc.chain.subscribeNewHeads((head) => {767 unsubscribe();768 resolve(head.number.toNumber());769 });770 });771}772773export async function774scheduleTransferExpectSuccess(775 collectionId: number,776 tokenId: number,777 sender: IKeyringPair,778 recipient: IKeyringPair,779 value: number | bigint = 1,780 blockTimeMs: number,781 blockSchedule: number,782) {783 await usingApi(async (api: ApiPromise) => {784 const blockNumber: number | undefined = await getBlockNumber(api);785 const expectedBlockNumber = blockNumber + blockSchedule;786787 expect(blockNumber).to.be.greaterThan(0);788 const transferTx = await api.tx.nft.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value); 789 const scheduleTx = await api.tx.scheduler.schedule(expectedBlockNumber, null, 0, transferTx);790791 await submitTransactionAsync(sender, scheduleTx);792793 const recipientBalanceBefore = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());794795 const nftItemDataBefore = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as any as ITokenDataType;796 expect(toSubstrateAddress(nftItemDataBefore.Owner)).to.be.equal(sender.address);797798 799 await new Promise(resolve => setTimeout(resolve, blockTimeMs * (blockSchedule + 1)));800801 const recipientBalanceAfter = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());802803 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;804 expect(toSubstrateAddress(nftItemData.Owner)).to.be.equal(recipient.address);805 expect(recipientBalanceAfter.toNumber()).to.be.equal(recipientBalanceBefore.toNumber());806 });807}808809810export async function811transferExpectSuccess(812 collectionId: number,813 tokenId: number,814 sender: IKeyringPair,815 recipient: IKeyringPair | CrossAccountId,816 value: number | bigint = 1,817 type = 'NFT',818) {819 await usingApi(async (api: ApiPromise) => {820 const to = normalizeAccountId(recipient);821822 let balanceBefore = new BN(0);823 if (type === 'Fungible') {824 balanceBefore = await api.query.nft.balance(collectionId, toSubstrateAddress(to)) as unknown as BN;825 }826 const transferTx = api.tx.nft.transfer(to, collectionId, tokenId, value);827 const events = await submitTransactionAsync(sender, transferTx);828 const result = getTransferResult(events);829 830 expect(result.success).to.be.true;831 expect(result.collectionId).to.be.equal(collectionId);832 expect(result.itemId).to.be.equal(tokenId);833 expect(result.sender).to.be.deep.equal(normalizeAccountId(sender.address));834 expect(result.recipient).to.be.deep.equal(to);835 expect(result.value.toString()).to.be.equal(value.toString());836 if (type === 'NFT') {837 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;838 expect(nftItemData.Owner).to.be.deep.equal(to);839 }840 if (type === 'Fungible') {841 const balanceAfter = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to)) as any).Value as unknown as BN;842 expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(value.toString());843 }844 if (type === 'ReFungible') {845 const nftItemData =846 (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON() as unknown as IReFungibleTokenDataType;847 expect(nftItemData.Owner[0].Owner).to.be.deep.equal(to);848 expect(nftItemData.Owner[0].Fraction.toString()).to.be.equal(value.toString());849 }850 });851}852853export async function854transferExpectFailure(855 collectionId: number,856 tokenId: number,857 sender: IKeyringPair,858 recipient: IKeyringPair,859 value: number | bigint = 1,860) {861 await usingApi(async (api: ApiPromise) => {862 const transferTx = api.tx.nft.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);863 const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;864 if (events && Array.isArray(events)) {865 const result = getCreateCollectionResult(events);866 867 expect(result.success).to.be.false;868 }869 });870}871872export async function873approveExpectFail(874 collectionId: number,875 tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number | bigint = 1,876) {877 await usingApi(async (api: ApiPromise) => {878 const approveNftTx = api.tx.nft.approve(normalizeAccountId(approved.address), collectionId, tokenId, amount);879 const events = await expect(submitTransactionExpectFailAsync(owner, approveNftTx)).to.be.rejected;880 const result = getCreateCollectionResult(events);881 882 expect(result.success).to.be.false;883 });884}885886export async function getFungibleBalance(887 collectionId: number,888 owner: string,889) {890 return await usingApi(async (api) => {891 const response = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON() as unknown as { Value: string };892 return BigInt(response.Value);893 });894}895896export async function createFungibleItemExpectSuccess(897 sender: IKeyringPair,898 collectionId: number,899 data: CreateFungibleData,900 owner: CrossAccountId | string = sender.address,901) {902 return await usingApi(async (api) => {903 const tx = api.tx.nft.createItem(collectionId, normalizeAccountId(owner), { Fungible: data });904905 const events = await submitTransactionAsync(sender, tx);906 const result = getCreateItemResult(events);907908 expect(result.success).to.be.true;909 return result.itemId;910 });911}912913export async function createItemExpectSuccess(sender: IKeyringPair, collectionId: number, createMode: string, owner: CrossAccountId | string = sender.address) {914 let newItemId = 0;915 await usingApi(async (api) => {916 const to = normalizeAccountId(owner);917 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);918 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to))).toJSON();919 const AItemBalance = new BigNumber(Aitem.Value);920921 let tx;922 if (createMode === 'Fungible') {923 const createData = { fungible: { value: 10 } };924 tx = api.tx.nft.createItem(collectionId, to, createData);925 } else if (createMode === 'ReFungible') {926 const createData = { refungible: { const_data: [], variable_data: [], pieces: 100 } };927 tx = api.tx.nft.createItem(collectionId, to, createData);928 } else {929 const createData = { nft: { const_data: [], variable_data: [] } };930 tx = api.tx.nft.createItem(collectionId, to, createData);931 }932933 const events = await submitTransactionAsync(sender, tx);934 const result = getCreateItemResult(events);935936 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);937 const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to))).toJSON();938 const BItemBalance = new BigNumber(Bitem.Value);939940 941 942 expect(result.success).to.be.true;943 if (createMode === 'Fungible') {944 expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);945 } else {946 expect(BItemCount).to.be.equal(AItemCount + 1);947 }948 expect(collectionId).to.be.equal(result.collectionId);949 expect(BItemCount.toString()).to.be.equal(result.itemId.toString());950 expect(to).to.be.deep.equal(result.recipient);951 newItemId = result.itemId;952 });953 return newItemId;954}955956export async function createItemExpectFailure(sender: IKeyringPair, collectionId: number, createMode: string, owner: string = sender.address) {957 await usingApi(async (api) => {958 const tx = api.tx.nft.createItem(collectionId, normalizeAccountId(owner), createMode);959 960 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;961 const result = getCreateItemResult(events);962963 expect(result.success).to.be.false;964 });965}966967export async function setPublicAccessModeExpectSuccess(968 sender: IKeyringPair, collectionId: number,969 accessMode: 'Normal' | 'WhiteList',970) {971 await usingApi(async (api) => {972973 974 const tx = api.tx.nft.setPublicAccessMode(collectionId, accessMode);975 const events = await submitTransactionAsync(sender, tx);976 const result = getGenericResult(events);977978 979 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();980981 982 983 expect(result.success).to.be.true;984 expect(collection.Access).to.be.equal(accessMode);985 });986}987988export async function setPublicAccessModeExpectFail(989 sender: IKeyringPair, collectionId: number,990 accessMode: 'Normal' | 'WhiteList',991) {992 await usingApi(async (api) => {993994 995 const tx = api.tx.nft.setPublicAccessMode(collectionId, accessMode);996 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;997 const result = getGenericResult(events);998999 1000 1001 expect(result.success).to.be.false;1002 });1003}10041005export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {1006 await setPublicAccessModeExpectSuccess(sender, collectionId, 'WhiteList');1007}10081009export async function enableWhiteListExpectFail(sender: IKeyringPair, collectionId: number) {1010 await setPublicAccessModeExpectFail(sender, collectionId, 'WhiteList');1011}10121013export async function disableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {1014 await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal');1015}10161017export async function setMintPermissionExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {1018 await usingApi(async (api) => {10191020 1021 const tx = api.tx.nft.setMintPermission(collectionId, enabled);1022 const events = await submitTransactionAsync(sender, tx);1023 const result = getGenericResult(events);10241025 1026 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();10271028 1029 1030 expect(result.success).to.be.true;1031 expect(collection.MintMode).to.be.equal(enabled);1032 });1033}10341035export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {1036 await setMintPermissionExpectSuccess(sender, collectionId, true);1037}10381039export async function setMintPermissionExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {1040 await usingApi(async (api) => {1041 1042 const tx = api.tx.nft.setMintPermission(collectionId, enabled);1043 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1044 const result = getCreateCollectionResult(events);1045 1046 expect(result.success).to.be.false;1047 });1048}10491050export async function isWhitelisted(collectionId: number, address: string) {1051 let whitelisted = false;1052 await usingApi(async (api) => {1053 whitelisted = (await api.query.nft.whiteList(collectionId, address)).toJSON() as unknown as boolean;1054 });1055 return whitelisted;1056}10571058export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1059 await usingApi(async (api) => {10601061 const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();10621063 1064 const tx = api.tx.nft.addToWhiteList(collectionId, normalizeAccountId(address));1065 const events = await submitTransactionAsync(sender, tx);1066 const result = getGenericResult(events);10671068 const whiteListedAfter = (await api.query.nft.whiteList(collectionId, address)).toJSON();10691070 1071 1072 expect(result.success).to.be.true;1073 1074 expect(whiteListedBefore).to.be.false;1075 1076 expect(whiteListedAfter).to.be.true;1077 });1078}10791080export async function addToWhiteListExpectFail(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1081 await usingApi(async (api) => {1082 1083 const tx = api.tx.nft.addToWhiteList(collectionId, normalizeAccountId(address));1084 const events = await expect(submitTransactionAsync(sender, tx)).to.be.rejected;1085 const result = getGenericResult(events);10861087 1088 1089 expect(result.success).to.be.false;1090 });1091}10921093export async function removeFromWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1094 await usingApi(async (api) => {1095 1096 const tx = api.tx.nft.removeFromWhiteList(collectionId, normalizeAccountId(address));1097 const events = await submitTransactionAsync(sender, tx);1098 const result = getGenericResult(events);10991100 1101 1102 expect(result.success).to.be.true;1103 });1104}11051106export async function removeFromWhiteListExpectFailure(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1107 await usingApi(async (api) => {1108 1109 const tx = api.tx.nft.removeFromWhiteList(collectionId, normalizeAccountId(address));1110 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1111 const result = getGenericResult(events);11121113 1114 1115 expect(result.success).to.be.false;1116 });1117}11181119export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)1120 : Promise<ICollectionInterface | null> => {1121 return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;1122};11231124export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {1125 1126 return (await api.query.nft.createdCollectionCount() as unknown as BN).toNumber();1127};11281129export async function queryCollectionExpectSuccess(collectionId: number): Promise<ICollectionInterface> {1130 return await usingApi(async (api) => {1131 return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;1132 });1133}11341135export async function queryNftOwner(api: ApiPromise, collectionId: number, tokenId: number): Promise<CrossAccountId> {1136 return normalizeAccountId((await api.query.nft.nftItemList(collectionId, tokenId) as any).toJSON().Owner);1137}