123456import { ApiPromise, Keyring } from '@polkadot/api';7import { Enum, Struct } from '@polkadot/types/codec';8import type { AccountId, BlockNumber, Call, EventRecord } from '@polkadot/types/interfaces';9import { u128 } from '@polkadot/types/primitive';10import { IKeyringPair } from '@polkadot/types/types';11import { evmToAddress } from '@polkadot/util-crypto';12import { BigNumber } from 'bignumber.js';13import BN from 'bn.js';14import chai from 'chai';15import chaiAsPromised from 'chai-as-promised';16import { alicesPublicKey, nullPublicKey } from '../accounts';17import privateKey from '../substrate/privateKey';18import { default as usingApi, submitTransactionAsync, submitTransactionExpectFailAsync } from '../substrate/substrate-api';19import { ICollectionInterface } from '../types';20import { hexToStr, strToUTF16, utf16ToStr } from './util';21import { Compact, Option, Raw, Vec } from '@polkadot/types/codec';222324chai.use(chaiAsPromised);25const expect = chai.expect;2627export type CrossAccountId = {28 substrate: string,29} | {30 ethereum: string,31};32export function normalizeAccountId(input: string | AccountId | CrossAccountId | IKeyringPair): CrossAccountId {33 if (typeof input === 'string')34 return { substrate: input };35 if ('address' in input) {36 return { substrate: input.address };37 }38 if ('ethereum' in input) {39 input.ethereum = input.ethereum.toLowerCase();40 return input;41 }42 if ('substrate' in input) {43 return input;44 }4546 47 return {substrate: input.toString()}48}49export function toSubstrateAddress(input: string | CrossAccountId | IKeyringPair): string {50 input = normalizeAccountId(input);51 if ('substrate' in input) {52 return input.substrate;53 } else {54 return evmToAddress(input.ethereum);55 }56}5758export const U128_MAX = (1n << 128n) - 1n;5960type GenericResult = {61 success: boolean,62};6364interface CreateCollectionResult {65 success: boolean;66 collectionId: number;67}6869interface CreateItemResult {70 success: boolean;71 collectionId: number;72 itemId: number;73 recipient?: CrossAccountId;74}7576interface TransferResult {77 success: boolean;78 collectionId: number;79 itemId: number;80 sender?: CrossAccountId;81 recipient?: CrossAccountId;82 value: bigint;83}8485interface IReFungibleOwner {86 Fraction: BN;87 Owner: number[];88}8990interface ITokenDataType {91 Owner: number[];92 ConstData: number[];93 VariableData: number[];94}9596interface IFungibleTokenDataType {97 Value: BN;98}99100interface IGetMessage {101 checkMsgNftMethod: string;102 checkMsgTrsMethod: string;103 checkMsgSysMethod: string;104}105106export interface IReFungibleTokenDataType {107 Owner: IReFungibleOwner[];108 ConstData: number[];109 VariableData: number[];110}111112export function nftEventMessage(events: EventRecord[]): IGetMessage {113 let checkMsgNftMethod: string = '';114 let checkMsgTrsMethod: string = '';115 let checkMsgSysMethod: string = '';116 events.forEach(({ event: { method, section } }) => {117 if (section === 'nft') {118 checkMsgNftMethod = method;119 } else if (section === 'treasury') {120 checkMsgTrsMethod = method;121 } else if (section === 'system') {122 checkMsgSysMethod = method;123 } else { return null; }124 });125 const result: IGetMessage = {126 checkMsgNftMethod,127 checkMsgTrsMethod,128 checkMsgSysMethod,129 };130 return result;131}132133export function getGenericResult(events: EventRecord[]): GenericResult {134 const result: GenericResult = {135 success: false,136 };137 events.forEach(({ phase, event: { data, method, section } }) => {138 139 if (method === 'ExtrinsicSuccess') {140 result.success = true;141 }142 });143 return result;144}145146147148export function getCreateCollectionResult(events: EventRecord[]): CreateCollectionResult {149 let success = false;150 let collectionId: number = 0;151 events.forEach(({ phase, event: { data, method, section } }) => {152 153 if (method == 'ExtrinsicSuccess') {154 success = true;155 } else if ((section == 'nft') && (method == 'CollectionCreated')) {156 collectionId = parseInt(data[0].toString());157 }158 });159 const result: CreateCollectionResult = {160 success,161 collectionId,162 };163 return result;164}165166export function getCreateItemResult(events: EventRecord[]): CreateItemResult {167 let success = false;168 let collectionId: number = 0;169 let itemId: number = 0;170 let recipient;171 events.forEach(({ phase, event: { data, method, section } }) => {172 173 if (method == 'ExtrinsicSuccess') {174 success = true;175 } else if ((section == 'nft') && (method == 'ItemCreated')) {176 collectionId = parseInt(data[0].toString());177 itemId = parseInt(data[1].toString());178 recipient = data[2].toJSON();179 }180 });181 const result: CreateItemResult = {182 success,183 collectionId,184 itemId,185 recipient,186 };187 return result;188}189190export function getTransferResult(events: EventRecord[]): TransferResult {191 const result: TransferResult = {192 success: false,193 collectionId: 0,194 itemId: 0,195 value: 0n,196 };197198 events.forEach(({ event: { data, method, section } }) => {199 if (method === 'ExtrinsicSuccess') {200 result.success = true;201 } else if (section === 'nft' && method === 'Transfer') {202 result.collectionId = +data[0].toString();203 result.itemId = +data[1].toString();204 result.sender = data[2].toJSON() as CrossAccountId;205 result.recipient = data[3].toJSON() as CrossAccountId;206 result.value = BigInt(data[4].toString());207 }208 });209210 return result;211}212213interface Invalid {214 type: 'Invalid';215}216217interface Nft {218 type: 'NFT';219}220221interface Fungible {222 type: 'Fungible';223 decimalPoints: number;224}225226interface ReFungible {227 type: 'ReFungible';228}229230type CollectionMode = Nft | Fungible | ReFungible | Invalid;231232export type CreateCollectionParams = {233 mode: CollectionMode,234 name: string,235 description: string,236 tokenPrefix: string,237};238239const defaultCreateCollectionParams: CreateCollectionParams = {240 description: 'description',241 mode: { type: 'NFT' },242 name: 'name',243 tokenPrefix: 'prefix',244}245246export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {247 const { name, description, mode, tokenPrefix } = { ...defaultCreateCollectionParams, ...params };248249 let collectionId: number = 0;250 await usingApi(async (api) => {251 252 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);253254 255 const alicePrivateKey = privateKey('//Alice');256257 let modeprm = {};258 if (mode.type === 'NFT') {259 modeprm = { nft: null };260 } else if (mode.type === 'Fungible') {261 modeprm = { fungible: mode.decimalPoints };262 } else if (mode.type === 'ReFungible') {263 modeprm = { refungible: null };264 } else if (mode.type === 'Invalid') {265 modeprm = { invalid: null };266 }267268 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);269 const events = await submitTransactionAsync(alicePrivateKey, tx);270 const result = getCreateCollectionResult(events);271272 273 const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);274275 276 const collection: any = (await api.query.nft.collectionById(result.collectionId) as any).toJSON();277278 279 280 expect(result.success).to.be.true;281 expect(result.collectionId).to.be.equal(BcollectionCount);282 283 expect(collection).to.be.not.null;284 expect(BcollectionCount).to.be.equal(AcollectionCount + 1, 'Error: NFT collection NOT created.');285 expect(collection.Owner).to.be.equal(toSubstrateAddress(alicesPublicKey));286 expect(utf16ToStr(collection.Name)).to.be.equal(name);287 expect(utf16ToStr(collection.Description)).to.be.equal(description);288 expect(hexToStr(collection.TokenPrefix)).to.be.equal(tokenPrefix);289290 collectionId = result.collectionId;291 });292293 return collectionId;294}295296export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {297 const { name, description, mode, tokenPrefix } = { ...defaultCreateCollectionParams, ...params };298299 let modeprm = {};300 if (mode.type === 'NFT') {301 modeprm = { nft: null };302 } else if (mode.type === 'Fungible') {303 modeprm = { fungible: mode.decimalPoints };304 } else if (mode.type === 'ReFungible') {305 modeprm = { refungible: null };306 } else if (mode.type === 'Invalid') {307 modeprm = { invalid: null };308 }309310 await usingApi(async (api) => {311 312 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());313314 315 const alicePrivateKey = privateKey('//Alice');316 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);317 const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;318 const result = getCreateCollectionResult(events);319320 321 const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());322323 324 325 expect(result.success).to.be.false;326 expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');327 });328}329330export async function findUnusedAddress(api: ApiPromise, seedAddition = ''): Promise<IKeyringPair> {331 let bal = new BigNumber(0);332 let unused;333 do {334 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;335 const keyring = new Keyring({ type: 'sr25519' });336 unused = keyring.addFromUri(`//${randomSeed}`);337 bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());338 } while (bal.toFixed() != '0');339 return unused;340}341342export async function getAllowance(collectionId: number, tokenId: number, owner: string, approved: string) {343 return await usingApi(async (api) => {344 const bn = await api.query.nft.allowances(collectionId, [tokenId, owner, approved]) as unknown as BN;345 return BigInt(bn.toString());346 });347}348349export function findUnusedAddresses(api: ApiPromise, amount: number): Promise<IKeyringPair[]> {350 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(api, '_' + Date.now())));351}352353export async function findNotExistingCollection(api: ApiPromise): Promise<number> {354 const totalNumber = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10) as unknown as number;355 const newCollection: number = totalNumber + 1;356 return newCollection;357}358359function getDestroyResult(events: EventRecord[]): boolean {360 let success: boolean = false;361 events.forEach(({ phase, event: { data, method, section } }) => {362 363 if (method == 'ExtrinsicSuccess') {364 success = true;365 }366 });367 return success;368}369370export async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {371 await usingApi(async (api) => {372 373 const alicePrivateKey = privateKey(senderSeed);374 const tx = api.tx.nft.destroyCollection(collectionId);375 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;376 });377}378379export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {380 await usingApi(async (api) => {381 382 const alicePrivateKey = privateKey(senderSeed);383 const tx = api.tx.nft.destroyCollection(collectionId);384 const events = await submitTransactionAsync(alicePrivateKey, tx);385 const result = getDestroyResult(events);386387 388 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();389390 391 expect(result).to.be.true;392 expect(collection).to.be.null;393 });394}395396export async function queryCollectionLimits(collectionId: number) {397 return await usingApi(async (api) => {398 return ((await api.query.nft.collectionById(collectionId)).toJSON() as any).Limits;399 });400}401402export async function setCollectionLimitsExpectSuccess(sender: IKeyringPair, collectionId: number, limits: any) {403 await usingApi(async (api) => {404 const oldLimits = await queryCollectionLimits(collectionId);405 const newLimits = { ...oldLimits as any, ...limits };406 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);407 const events = await submitTransactionAsync(sender, tx);408 const result = getGenericResult(events);409410 expect(result.success).to.be.true;411 });412}413414export async function setCollectionLimitsExpectFailure(sender: IKeyringPair, collectionId: number, limits: any) {415 await usingApi(async (api) => {416 const oldLimits = await queryCollectionLimits(collectionId);417 const newLimits = { ...oldLimits as any, ...limits };418 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);419 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;420 const result = getGenericResult(events);421422 expect(result.success).to.be.false;423 });424}425426export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {427 await usingApi(async (api) => {428429 430 const alicePrivateKey = privateKey('//Alice');431 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);432 const events = await submitTransactionAsync(alicePrivateKey, tx);433 const result = getGenericResult(events);434435 436 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();437438 439 expect(result.success).to.be.true;440 expect(collection.Sponsorship).to.deep.equal({441 unconfirmed: sponsor,442 });443 });444}445446export async function removeCollectionSponsorExpectSuccess(collectionId: number) {447 await usingApi(async (api) => {448449 450 const alicePrivateKey = privateKey('//Alice');451 const tx = api.tx.nft.removeCollectionSponsor(collectionId);452 const events = await submitTransactionAsync(alicePrivateKey, tx);453 const result = getGenericResult(events);454455 456 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();457458 459 expect(result.success).to.be.true;460 expect(collection.Sponsorship).to.be.deep.equal({ disabled: null });461 });462}463464export async function removeCollectionSponsorExpectFailure(collectionId: number) {465 await usingApi(async (api) => {466467 468 const alicePrivateKey = privateKey('//Alice');469 const tx = api.tx.nft.removeCollectionSponsor(collectionId);470 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;471 });472}473474export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed: string = '//Alice') {475 await usingApi(async (api) => {476477 478 const alicePrivateKey = privateKey(senderSeed);479 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);480 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;481 });482}483484export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {485 await usingApi(async (api) => {486487 488 const sender = privateKey(senderSeed);489 const tx = api.tx.nft.confirmSponsorship(collectionId);490 const events = await submitTransactionAsync(sender, tx);491 const result = getGenericResult(events);492493 494 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();495496 497 expect(result.success).to.be.true;498 expect(collection.Sponsorship).to.be.deep.equal({499 confirmed: sender.address,500 });501 });502}503504505export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed: string = '//Alice') {506 await usingApi(async (api) => {507508 509 const sender = privateKey(senderSeed);510 const tx = api.tx.nft.confirmSponsorship(collectionId);511 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;512 });513}514515export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {516 await usingApi(async (api) => {517 const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);518 const events = await submitTransactionAsync(sender, tx);519 const result = getGenericResult(events);520521 expect(result.success).to.be.true;522 });523}524525export async function enableContractSponsoringExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {526 await usingApi(async (api) => {527 const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);528 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;529 const result = getGenericResult(events);530531 expect(result.success).to.be.false;532 });533}534535export async function setContractSponsoringRateLimitExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {536 await usingApi(async (api) => {537 const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);538 const events = await submitTransactionAsync(sender, tx);539 const result = getGenericResult(events);540541 expect(result.success).to.be.true;542 });543}544545export async function setContractSponsoringRateLimitExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {546 await usingApi(async (api) => {547 const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);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 toggleContractWhitelistExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enabled: boolean) {556 await usingApi(async (api) => {557 const tx = api.tx.nft.toggleContractWhiteList(contractAddress, true);558 const events = await submitTransactionAsync(sender, tx);559 const result = getGenericResult(events);560561 expect(result.success).to.be.true;562 });563}564565export async function isWhitelistedInContract(contractAddress: AccountId | string, user: string) {566 let whitelisted: boolean = false;567 await usingApi(async (api) => {568 whitelisted = (await api.query.nft.contractWhiteList(contractAddress, user)).toJSON() as boolean;569 });570 return whitelisted;571}572573export async function addToContractWhiteListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {574 await usingApi(async (api) => {575 const tx = api.tx.nft.addToContractWhiteList(contractAddress.toString(), user.toString());576 const events = await submitTransactionAsync(sender, tx);577 const result = getGenericResult(events);578579 expect(result.success).to.be.true;580 });581}582583export async function removeFromContractWhiteListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {584 await usingApi(async (api) => {585 const tx = api.tx.nft.removeFromContractWhiteList(contractAddress.toString(), user.toString());586 const events = await submitTransactionAsync(sender, tx);587 const result = getGenericResult(events);588589 expect(result.success).to.be.true;590 });591}592593export async function removeFromContractWhiteListExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {594 await usingApi(async (api) => {595 const tx = api.tx.nft.removeFromContractWhiteList(contractAddress.toString(), user.toString());596 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;597 const result = getGenericResult(events);598599 expect(result.success).to.be.false;600 });601}602603export async function setVariableMetaDataExpectSuccess(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {604 await usingApi(async (api) => {605 const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));606 const events = await submitTransactionAsync(sender, tx);607 const result = getGenericResult(events);608609 expect(result.success).to.be.true;610 });611}612613export async function setVariableMetaDataExpectFailure(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {614 await usingApi(async (api) => {615 const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));616 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;617 });618}619620export async function setOffchainSchemaExpectSuccess(sender: IKeyringPair, collectionId: number, data: number[]) {621 await usingApi(async (api) => {622 const tx = api.tx.nft.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));623 const events = await submitTransactionAsync(sender, tx);624 const result = getGenericResult(events);625626 expect(result.success).to.be.true;627 });628}629630export async function setOffchainSchemaExpectFailure(sender: IKeyringPair, collectionId: number, data: number[]) {631 await usingApi(async (api) => {632 const tx = api.tx.nft.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));633 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;634 });635}636637export interface CreateFungibleData {638 readonly Value: bigint;639}640641export interface CreateReFungibleData { }642export interface CreateNftData { }643644export type CreateItemData = {645 NFT: CreateNftData;646} | {647 Fungible: CreateFungibleData;648} | {649 ReFungible: CreateReFungibleData;650};651652export async function burnItemExpectSuccess(owner: IKeyringPair, collectionId: number, tokenId: number, value = 0) {653 await usingApi(async (api) => {654 const tx = api.tx.nft.burnItem(collectionId, tokenId, value);655 const events = await submitTransactionAsync(owner, tx);656 const result = getGenericResult(events);657 658 const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();659 660 661 expect(result.success).to.be.true;662 663 expect(item).to.be.null;664 });665}666667export async function668 approveExpectSuccess(collectionId: number,669 tokenId: number, owner: IKeyringPair, approved: IKeyringPair | CrossAccountId | string, amount: number | bigint = 1) {670 await usingApi(async (api: ApiPromise) => {671 approved = normalizeAccountId(approved);672 const allowanceBefore =673 await api.query.nft.allowances(collectionId, [tokenId, owner.address, toSubstrateAddress(approved)]) as unknown as BN;674 const approveNftTx = api.tx.nft.approve(approved, collectionId, tokenId, amount);675 const events = await submitTransactionAsync(owner, approveNftTx);676 const result = getCreateItemResult(events);677 678 expect(result.success).to.be.true;679 const allowanceAfter =680 await api.query.nft.allowances(collectionId, [tokenId, owner.address, toSubstrateAddress(approved)]) as unknown as BN;681 expect(allowanceAfter.sub(allowanceBefore).toString()).to.be.equal(amount.toString());682 });683}684685export async function686 transferFromExpectSuccess(collectionId: number,687 tokenId: number,688 accountApproved: IKeyringPair,689 accountFrom: IKeyringPair | CrossAccountId,690 accountTo: IKeyringPair | CrossAccountId,691 value: number | bigint = 1,692 type: string = 'NFT') {693 await usingApi(async (api: ApiPromise) => {694 const to = normalizeAccountId(accountTo);695 let balanceBefore = new BN(0);696 if (type === 'Fungible') {697 balanceBefore = await api.query.nft.balance(collectionId, toSubstrateAddress(to)) as unknown as BN;698 }699 const transferFromTx = api.tx.nft.transferFrom(700 normalizeAccountId(accountFrom), to, collectionId, tokenId, value);701 const events = await submitTransactionAsync(accountApproved, transferFromTx);702 const result = getCreateItemResult(events);703 704 expect(result.success).to.be.true;705 if (type === 'NFT') {706 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId) as any).toJSON() as ITokenDataType;707 expect(nftItemData.Owner).to.be.deep.equal(to);708 }709 if (type === 'Fungible') {710 const balanceAfter = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to)) as any).Value as unknown as BN;711 expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(value.toString());712 }713 if (type === 'ReFungible') {714 const nftItemData =715 (await api.query.nft.reFungibleItemList(collectionId, tokenId) as any).toJSON() as IReFungibleTokenDataType;716 expect(nftItemData.Owner[0].Owner).to.be.deep.equal(normalizeAccountId(to));717 expect(nftItemData.Owner[0].Fraction).to.be.equal(value);718 }719 });720}721722export async function723 transferFromExpectFail(collectionId: number,724 tokenId: number,725 accountApproved: IKeyringPair,726 accountFrom: IKeyringPair,727 accountTo: IKeyringPair,728 value: number | bigint = 1) {729 await usingApi(async (api: ApiPromise) => {730 const transferFromTx = api.tx.nft.transferFrom(731 normalizeAccountId(accountFrom.address), normalizeAccountId(accountTo.address), collectionId, tokenId, value);732 const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;733 const result = getCreateCollectionResult(events);734 735 expect(result.success).to.be.false;736 });737}738739async function getBlockNumber(api: ApiPromise): Promise<number> {740 return new Promise<number>(async (resolve, reject) => {741 const unsubscribe = await api.rpc.chain.subscribeNewHeads((head) => {742 unsubscribe();743 resolve(head.number.toNumber());744 });745 });746}747748export async function749scheduleTransferExpectSuccess(collectionId: number,750 tokenId: number,751 sender: IKeyringPair,752 recipient: IKeyringPair,753 value: number | bigint = 1,754 type: string = 'NFT') {755 await usingApi(async (api: ApiPromise) => {756 let balanceBefore = new BN(0);757758759 let blockNumber: number | undefined = await getBlockNumber(api);760 let expectedBlockNumber = blockNumber + 2;761762 expect(blockNumber).to.be.greaterThan(0);763 const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value); 764 const scheduleTx = await api.tx.scheduler.schedule(expectedBlockNumber, null, 0, transferTx);765766 const events = await submitTransactionAsync(sender, scheduleTx);767768 const sponsorBalanceBefore = new BigNumber((await api.query.system.account(sender.address)).data.free.toString());769 const recipientBalanceBefore = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());770771 const nftItemDataBefore = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;772 expect(nftItemDataBefore.Owner.toString()).to.be.equal(sender.address);773774 775 await new Promise(resolve => setTimeout(resolve, 6000 * 2));776777 const sponsorBalanceAfter = new BigNumber((await api.query.system.account(sender.address)).data.free.toString());778 const recipientBalanceAfter = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());779780 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;781 expect(nftItemData.Owner.toString()).to.be.equal(recipient.address);782 expect(recipientBalanceAfter.toNumber()).to.be.equal(recipientBalanceBefore.toNumber());783 });784}785786787export async function788 transferExpectSuccess(collectionId: number,789 tokenId: number,790 sender: IKeyringPair,791 recipient: IKeyringPair | CrossAccountId,792 value: number | bigint = 1,793 type: string = 'NFT') {794 await usingApi(async (api: ApiPromise) => {795 const to = normalizeAccountId(recipient);796797 let balanceBefore = new BN(0);798 if (type === 'Fungible') {799 balanceBefore = await api.query.nft.balance(collectionId, toSubstrateAddress(to)) as unknown as BN;800 }801 const transferTx = api.tx.nft.transfer(to, collectionId, tokenId, value);802 const events = await submitTransactionAsync(sender, transferTx);803 const result = getTransferResult(events);804 805 expect(result.success).to.be.true;806 expect(result.collectionId).to.be.equal(collectionId);807 expect(result.itemId).to.be.equal(tokenId);808 expect(result.sender).to.be.deep.equal(normalizeAccountId(sender.address));809 expect(result.recipient).to.be.deep.equal(to);810 expect(result.value.toString()).to.be.equal(value.toString());811 if (type === 'NFT') {812 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;813 expect(nftItemData.Owner).to.be.deep.equal(to);814 }815 if (type === 'Fungible') {816 const balanceAfter = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to)) as any).Value as unknown as BN;817 expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(value.toString());818 }819 if (type === 'ReFungible') {820 const nftItemData =821 (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON() as unknown as IReFungibleTokenDataType;822 expect(nftItemData.Owner[0].Owner).to.be.deep.equal(to);823 expect(nftItemData.Owner[0].Fraction.toString()).to.be.equal(value.toString());824 }825 });826}827828export async function829 transferExpectFail(collectionId: number,830 tokenId: number,831 sender: IKeyringPair,832 recipient: IKeyringPair,833 value: number | bigint = 1,834 type: string = 'NFT') {835 await usingApi(async (api: ApiPromise) => {836 const transferTx = api.tx.nft.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);837 const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;838 if (events && Array.isArray(events)) {839 const result = getCreateCollectionResult(events);840 841 expect(result.success).to.be.false;842 }843 });844}845846export async function847 approveExpectFail(collectionId: number,848 tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number | bigint = 1) {849 await usingApi(async (api: ApiPromise) => {850 const approveNftTx = api.tx.nft.approve(normalizeAccountId(approved.address), collectionId, tokenId, amount);851 const events = await expect(submitTransactionExpectFailAsync(owner, approveNftTx)).to.be.rejected;852 const result = getCreateCollectionResult(events);853 854 expect(result.success).to.be.false;855 });856}857858export async function getFungibleBalance(859 collectionId: number,860 owner: string,861) {862 return await usingApi(async (api) => {863 const response = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON() as unknown as { Value: string };864 return BigInt(response.Value);865 });866}867868export async function createFungibleItemExpectSuccess(869 sender: IKeyringPair,870 collectionId: number,871 data: CreateFungibleData,872 owner: CrossAccountId | string = sender.address,873) {874 return await usingApi(async (api) => {875 const tx = api.tx.nft.createItem(collectionId, normalizeAccountId(owner), { Fungible: data });876877 const events = await submitTransactionAsync(sender, tx);878 const result = getCreateItemResult(events);879880 expect(result.success).to.be.true;881 return result.itemId;882 });883}884885export async function createItemExpectSuccess(886 sender: IKeyringPair, collectionId: number, createMode: string, owner: CrossAccountId | string = sender.address) {887 let newItemId: number = 0;888 await usingApi(async (api) => {889 const to = normalizeAccountId(owner);890 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);891 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to))).toJSON();892 const AItemBalance = new BigNumber(Aitem.Value);893894 let tx;895 if (createMode === 'Fungible') {896 const createData = { fungible: { value: 10 } };897 tx = api.tx.nft.createItem(collectionId, to, createData);898 } else if (createMode === 'ReFungible') {899 const createData = { refungible: { const_data: [], variable_data: [], pieces: 100 } };900 tx = api.tx.nft.createItem(collectionId, to, createData);901 } else {902 tx = api.tx.nft.createItem(collectionId, to, createMode);903 }904905 const events = await submitTransactionAsync(sender, tx);906 const result = getCreateItemResult(events);907908 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);909 const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to))).toJSON();910 const BItemBalance = new BigNumber(Bitem.Value);911912 913 914 expect(result.success).to.be.true;915 if (createMode === 'Fungible') {916 expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);917 } else {918 expect(BItemCount).to.be.equal(AItemCount + 1);919 }920 expect(collectionId).to.be.equal(result.collectionId);921 expect(BItemCount.toString()).to.be.equal(result.itemId.toString());922 expect(to).to.be.deep.equal(result.recipient);923 newItemId = result.itemId;924 });925 return newItemId;926}927928export async function createItemExpectFailure(929 sender: IKeyringPair, collectionId: number, createMode: string, owner: string = sender.address) {930 await usingApi(async (api) => {931 const tx = api.tx.nft.createItem(collectionId, normalizeAccountId(owner), createMode);932 933 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;934 const result = getCreateItemResult(events);935936 expect(result.success).to.be.false;937 });938}939940export async function setPublicAccessModeExpectSuccess(941 sender: IKeyringPair, collectionId: number,942 accessMode: 'Normal' | 'WhiteList',943) {944 await usingApi(async (api) => {945946 947 const tx = api.tx.nft.setPublicAccessMode(collectionId, accessMode);948 const events = await submitTransactionAsync(sender, tx);949 const result = getGenericResult(events);950951 952 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();953954 955 956 expect(result.success).to.be.true;957 expect(collection.Access).to.be.equal(accessMode);958 });959}960961export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {962 await setPublicAccessModeExpectSuccess(sender, collectionId, 'WhiteList');963}964965export async function disableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {966 await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal');967}968969export async function setMintPermissionExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {970 await usingApi(async (api) => {971972 973 const tx = api.tx.nft.setMintPermission(collectionId, enabled);974 const events = await submitTransactionAsync(sender, tx);975 const result = getGenericResult(events);976977 978 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();979980 981 982 expect(result.success).to.be.true;983 expect(collection.MintMode).to.be.equal(enabled);984 });985}986987export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {988 await setMintPermissionExpectSuccess(sender, collectionId, true);989}990991export async function setMintPermissionExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {992 await usingApi(async (api) => {993 994 const tx = api.tx.nft.setMintPermission(collectionId, enabled);995 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;996 const result = getCreateCollectionResult(events);997 998 expect(result.success).to.be.false;999 });1000}10011002export async function isWhitelisted(collectionId: number, address: string) {1003 let whitelisted: boolean = false;1004 await usingApi(async (api) => {1005 whitelisted = (await api.query.nft.whiteList(collectionId, address)).toJSON() as unknown as boolean;1006 });1007 return whitelisted;1008}10091010export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1011 await usingApi(async (api) => {10121013 const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();10141015 1016 const tx = api.tx.nft.addToWhiteList(collectionId, normalizeAccountId(address));1017 const events = await submitTransactionAsync(sender, tx);1018 const result = getGenericResult(events);10191020 const whiteListedAfter = (await api.query.nft.whiteList(collectionId, address)).toJSON();10211022 1023 1024 expect(result.success).to.be.true;1025 1026 expect(whiteListedBefore).to.be.false;1027 1028 expect(whiteListedAfter).to.be.true;1029 });1030}10311032export async function removeFromWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1033 await usingApi(async (api) => {1034 1035 const tx = api.tx.nft.removeFromWhiteList(collectionId, normalizeAccountId(address));1036 const events = await submitTransactionAsync(sender, tx);1037 const result = getGenericResult(events);10381039 1040 1041 expect(result.success).to.be.true;1042 });1043}10441045export async function removeFromWhiteListExpectFailure(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1046 await usingApi(async (api) => {1047 1048 const tx = api.tx.nft.removeFromWhiteList(collectionId, normalizeAccountId(address));1049 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1050 const result = getGenericResult(events);10511052 1053 1054 expect(result.success).to.be.false;1055 });1056}10571058export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)1059 : Promise<ICollectionInterface | null> => {1060 return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;1061};10621063export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {1064 1065 return (await api.query.nft.createdCollectionCount() as unknown as BN).toNumber();1066};10671068export async function queryCollectionExpectSuccess(collectionId: number): Promise<ICollectionInterface> {1069 return await usingApi(async (api) => {1070 return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;1071 });1072}