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 | 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 }41 return input;42}43export function toSubstrateAddress(input: string | CrossAccountId | IKeyringPair): string {44 input = normalizeAccountId(input);45 if ('substrate' in input) {46 return input.substrate;47 } else {48 return evmToAddress(input.ethereum);49 }50}5152export const U128_MAX = (1n << 128n) - 1n;5354type GenericResult = {55 success: boolean,56};5758interface CreateCollectionResult {59 success: boolean;60 collectionId: number;61}6263interface CreateItemResult {64 success: boolean;65 collectionId: number;66 itemId: number;67 recipient?: CrossAccountId;68}6970interface TransferResult {71 success: boolean;72 collectionId: number;73 itemId: number;74 sender?: CrossAccountId;75 recipient?: CrossAccountId;76 value: bigint;77}7879interface IReFungibleOwner {80 Fraction: BN;81 Owner: number[];82}8384interface ITokenDataType {85 Owner: number[];86 ConstData: number[];87 VariableData: number[];88}8990interface IFungibleTokenDataType {91 Value: BN;92}9394interface IGetMessage {95 checkMsgNftMethod: string;96 checkMsgTrsMethod: string;97 checkMsgSysMethod: string;98}99100export interface IReFungibleTokenDataType {101 Owner: IReFungibleOwner[];102 ConstData: number[];103 VariableData: number[];104}105106export function nftEventMessage(events: EventRecord[]): IGetMessage {107 let checkMsgNftMethod: string = '';108 let checkMsgTrsMethod: string = '';109 let checkMsgSysMethod: string = '';110 events.forEach(({ event: { method, section } }) => {111 if (section === 'nft') {112 checkMsgNftMethod = method;113 } else if (section === 'treasury') {114 checkMsgTrsMethod = method;115 } else if (section === 'system') {116 checkMsgSysMethod = method;117 } else { return null; }118 });119 const result: IGetMessage = {120 checkMsgNftMethod,121 checkMsgTrsMethod,122 checkMsgSysMethod,123 };124 return result;125}126127export function getGenericResult(events: EventRecord[]): GenericResult {128 const result: GenericResult = {129 success: false,130 };131 events.forEach(({ phase, event: { data, method, section } }) => {132 133 if (method === 'ExtrinsicSuccess') {134 result.success = true;135 }136 });137 return result;138}139140141142export function getCreateCollectionResult(events: EventRecord[]): CreateCollectionResult {143 let success = false;144 let collectionId: number = 0;145 events.forEach(({ phase, event: { data, method, section } }) => {146 147 if (method == 'ExtrinsicSuccess') {148 success = true;149 } else if ((section == 'nft') && (method == 'CollectionCreated')) {150 collectionId = parseInt(data[0].toString());151 }152 });153 const result: CreateCollectionResult = {154 success,155 collectionId,156 };157 return result;158}159160export function getCreateItemResult(events: EventRecord[]): CreateItemResult {161 let success = false;162 let collectionId: number = 0;163 let itemId: number = 0;164 let recipient;165 events.forEach(({ phase, event: { data, method, section } }) => {166 167 if (method == 'ExtrinsicSuccess') {168 success = true;169 } else if ((section == 'nft') && (method == 'ItemCreated')) {170 collectionId = parseInt(data[0].toString());171 itemId = parseInt(data[1].toString());172 recipient = data[2].toJSON();173 }174 });175 const result: CreateItemResult = {176 success,177 collectionId,178 itemId,179 recipient,180 };181 return result;182}183184export function getTransferResult(events: EventRecord[]): TransferResult {185 const result: TransferResult = {186 success: false,187 collectionId: 0,188 itemId: 0,189 value: 0n,190 };191192 events.forEach(({ event: { data, method, section } }) => {193 if (method === 'ExtrinsicSuccess') {194 result.success = true;195 } else if (section === 'nft' && method === 'Transfer') {196 result.collectionId = +data[0].toString();197 result.itemId = +data[1].toString();198 result.sender = data[2].toJSON() as CrossAccountId;199 result.recipient = data[3].toJSON() as CrossAccountId;200 result.value = BigInt(data[4].toString());201 }202 });203204 return result;205}206207interface Invalid {208 type: 'Invalid';209}210211interface Nft {212 type: 'NFT';213}214215interface Fungible {216 type: 'Fungible';217 decimalPoints: number;218}219220interface ReFungible {221 type: 'ReFungible';222}223224type CollectionMode = Nft | Fungible | ReFungible | Invalid;225226export type CreateCollectionParams = {227 mode: CollectionMode,228 name: string,229 description: string,230 tokenPrefix: string,231};232233const defaultCreateCollectionParams: CreateCollectionParams = {234 description: 'description',235 mode: { type: 'NFT' },236 name: 'name',237 tokenPrefix: 'prefix',238}239240export async function createCollectionExpectSuccess(params: Partial<CreateCollectionParams> = {}): Promise<number> {241 const { name, description, mode, tokenPrefix } = { ...defaultCreateCollectionParams, ...params };242243 let collectionId: number = 0;244 await usingApi(async (api) => {245 246 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);247248 249 const alicePrivateKey = privateKey('//Alice');250251 let modeprm = {};252 if (mode.type === 'NFT') {253 modeprm = { nft: null };254 } else if (mode.type === 'Fungible') {255 modeprm = { fungible: mode.decimalPoints };256 } else if (mode.type === 'ReFungible') {257 modeprm = { refungible: null };258 } else if (mode.type === 'Invalid') {259 modeprm = { invalid: null };260 }261262 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);263 const events = await submitTransactionAsync(alicePrivateKey, tx);264 const result = getCreateCollectionResult(events);265266 267 const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10);268269 270 const collection: any = (await api.query.nft.collectionById(result.collectionId) as any).toJSON();271272 273 274 expect(result.success).to.be.true;275 expect(result.collectionId).to.be.equal(BcollectionCount);276 277 expect(collection).to.be.not.null;278 expect(BcollectionCount).to.be.equal(AcollectionCount + 1, 'Error: NFT collection NOT created.');279 expect(collection.Owner).to.be.equal(toSubstrateAddress(alicesPublicKey));280 expect(utf16ToStr(collection.Name)).to.be.equal(name);281 expect(utf16ToStr(collection.Description)).to.be.equal(description);282 expect(hexToStr(collection.TokenPrefix)).to.be.equal(tokenPrefix);283284 collectionId = result.collectionId;285 });286287 return collectionId;288}289290export async function createCollectionExpectFailure(params: Partial<CreateCollectionParams> = {}) {291 const { name, description, mode, tokenPrefix } = { ...defaultCreateCollectionParams, ...params };292293 let modeprm = {};294 if (mode.type === 'NFT') {295 modeprm = { nft: null };296 } else if (mode.type === 'Fungible') {297 modeprm = { fungible: mode.decimalPoints };298 } else if (mode.type === 'ReFungible') {299 modeprm = { refungible: null };300 } else if (mode.type === 'Invalid') {301 modeprm = { invalid: null };302 }303304 await usingApi(async (api) => {305 306 const AcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());307308 309 const alicePrivateKey = privateKey('//Alice');310 const tx = api.tx.nft.createCollection(strToUTF16(name), strToUTF16(description), strToUTF16(tokenPrefix), modeprm);311 const events = await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;312 const result = getCreateCollectionResult(events);313314 315 const BcollectionCount = parseInt((await api.query.nft.createdCollectionCount()).toString());316317 318 319 expect(result.success).to.be.false;320 expect(BcollectionCount).to.be.equal(AcollectionCount, 'Error: Collection with incorrect data created.');321 });322}323324export async function findUnusedAddress(api: ApiPromise, seedAddition = ''): Promise<IKeyringPair> {325 let bal = new BigNumber(0);326 let unused;327 do {328 const randomSeed = 'seed' + Math.floor(Math.random() * Math.floor(10000)) + seedAddition;329 const keyring = new Keyring({ type: 'sr25519' });330 unused = keyring.addFromUri(`//${randomSeed}`);331 bal = new BigNumber((await api.query.system.account(unused.address)).data.free.toString());332 } while (bal.toFixed() != '0');333 return unused;334}335336export async function getAllowance(collectionId: number, tokenId: number, owner: string, approved: string) {337 return await usingApi(async (api) => {338 const bn = await api.query.nft.allowances(collectionId, [tokenId, owner, approved]) as unknown as BN;339 return BigInt(bn.toString());340 });341}342343export function findUnusedAddresses(api: ApiPromise, amount: number): Promise<IKeyringPair[]> {344 return Promise.all(new Array(amount).fill(null).map(() => findUnusedAddress(api, '_' + Date.now())));345}346347export async function findNotExistingCollection(api: ApiPromise): Promise<number> {348 const totalNumber = parseInt((await api.query.nft.createdCollectionCount()).toString(), 10) as unknown as number;349 const newCollection: number = totalNumber + 1;350 return newCollection;351}352353function getDestroyResult(events: EventRecord[]): boolean {354 let success: boolean = false;355 events.forEach(({ phase, event: { data, method, section } }) => {356 357 if (method == 'ExtrinsicSuccess') {358 success = true;359 }360 });361 return success;362}363364export async function destroyCollectionExpectFailure(collectionId: number, senderSeed: string = '//Alice') {365 await usingApi(async (api) => {366 367 const alicePrivateKey = privateKey(senderSeed);368 const tx = api.tx.nft.destroyCollection(collectionId);369 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;370 });371}372373export async function destroyCollectionExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {374 await usingApi(async (api) => {375 376 const alicePrivateKey = privateKey(senderSeed);377 const tx = api.tx.nft.destroyCollection(collectionId);378 const events = await submitTransactionAsync(alicePrivateKey, tx);379 const result = getDestroyResult(events);380381 382 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();383384 385 expect(result).to.be.true;386 expect(collection).to.be.null;387 });388}389390export async function queryCollectionLimits(collectionId: number) {391 return await usingApi(async (api) => {392 return ((await api.query.nft.collectionById(collectionId)).toJSON() as any).Limits;393 });394}395396export async function setCollectionLimitsExpectSuccess(sender: IKeyringPair, collectionId: number, limits: any) {397 await usingApi(async (api) => {398 const oldLimits = await queryCollectionLimits(collectionId);399 const newLimits = { ...oldLimits as any, ...limits };400 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);401 const events = await submitTransactionAsync(sender, tx);402 const result = getGenericResult(events);403404 expect(result.success).to.be.true;405 });406}407408export async function setCollectionLimitsExpectFailure(sender: IKeyringPair, collectionId: number, limits: any) {409 await usingApi(async (api) => {410 const oldLimits = await queryCollectionLimits(collectionId);411 const newLimits = { ...oldLimits as any, ...limits };412 const tx = api.tx.nft.setCollectionLimits(collectionId, newLimits);413 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;414 const result = getGenericResult(events);415416 expect(result.success).to.be.false;417 });418}419420export async function setCollectionSponsorExpectSuccess(collectionId: number, sponsor: string) {421 await usingApi(async (api) => {422423 424 const alicePrivateKey = privateKey('//Alice');425 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);426 const events = await submitTransactionAsync(alicePrivateKey, tx);427 const result = getGenericResult(events);428429 430 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();431432 433 expect(result.success).to.be.true;434 expect(collection.Sponsorship).to.deep.equal({435 unconfirmed: sponsor,436 });437 });438}439440export async function removeCollectionSponsorExpectSuccess(collectionId: number) {441 await usingApi(async (api) => {442443 444 const alicePrivateKey = privateKey('//Alice');445 const tx = api.tx.nft.removeCollectionSponsor(collectionId);446 const events = await submitTransactionAsync(alicePrivateKey, tx);447 const result = getGenericResult(events);448449 450 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();451452 453 expect(result.success).to.be.true;454 expect(collection.Sponsorship).to.be.deep.equal({ disabled: null });455 });456}457458export async function removeCollectionSponsorExpectFailure(collectionId: number) {459 await usingApi(async (api) => {460461 462 const alicePrivateKey = privateKey('//Alice');463 const tx = api.tx.nft.removeCollectionSponsor(collectionId);464 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;465 });466}467468export async function setCollectionSponsorExpectFailure(collectionId: number, sponsor: string, senderSeed: string = '//Alice') {469 await usingApi(async (api) => {470471 472 const alicePrivateKey = privateKey(senderSeed);473 const tx = api.tx.nft.setCollectionSponsor(collectionId, sponsor);474 await expect(submitTransactionExpectFailAsync(alicePrivateKey, tx)).to.be.rejected;475 });476}477478export async function confirmSponsorshipExpectSuccess(collectionId: number, senderSeed: string = '//Alice') {479 await usingApi(async (api) => {480481 482 const sender = privateKey(senderSeed);483 const tx = api.tx.nft.confirmSponsorship(collectionId);484 const events = await submitTransactionAsync(sender, tx);485 const result = getGenericResult(events);486487 488 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();489490 491 expect(result.success).to.be.true;492 expect(collection.Sponsorship).to.be.deep.equal({493 confirmed: sender.address,494 });495 });496}497498499export async function confirmSponsorshipExpectFailure(collectionId: number, senderSeed: string = '//Alice') {500 await usingApi(async (api) => {501502 503 const sender = privateKey(senderSeed);504 const tx = api.tx.nft.confirmSponsorship(collectionId);505 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;506 });507}508509export async function enableContractSponsoringExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {510 await usingApi(async (api) => {511 const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);512 const events = await submitTransactionAsync(sender, tx);513 const result = getGenericResult(events);514515 expect(result.success).to.be.true;516 });517}518519export async function enableContractSponsoringExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, enable: boolean) {520 await usingApi(async (api) => {521 const tx = api.tx.nft.enableContractSponsoring(contractAddress, enable);522 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;523 const result = getGenericResult(events);524525 expect(result.success).to.be.false;526 });527}528529export async function setContractSponsoringRateLimitExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {530 await usingApi(async (api) => {531 const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);532 const events = await submitTransactionAsync(sender, tx);533 const result = getGenericResult(events);534535 expect(result.success).to.be.true;536 });537}538539export async function setContractSponsoringRateLimitExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, rateLimit: number) {540 await usingApi(async (api) => {541 const tx = api.tx.nft.setContractSponsoringRateLimit(contractAddress, rateLimit);542 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;543 const result = getGenericResult(events);544545 expect(result.success).to.be.false;546 });547}548549export async function toggleContractWhitelistExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, enabled: boolean) {550 await usingApi(async (api) => {551 const tx = api.tx.nft.toggleContractWhiteList(contractAddress, true);552 const events = await submitTransactionAsync(sender, tx);553 const result = getGenericResult(events);554555 expect(result.success).to.be.true;556 });557}558559export async function isWhitelistedInContract(contractAddress: AccountId | string, user: string) {560 let whitelisted: boolean = false;561 await usingApi(async (api) => {562 whitelisted = (await api.query.nft.contractWhiteList(contractAddress, user)).toJSON() as boolean;563 });564 return whitelisted;565}566567export async function addToContractWhiteListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {568 await usingApi(async (api) => {569 const tx = api.tx.nft.addToContractWhiteList(contractAddress.toString(), user.toString());570 const events = await submitTransactionAsync(sender, tx);571 const result = getGenericResult(events);572573 expect(result.success).to.be.true;574 });575}576577export async function removeFromContractWhiteListExpectSuccess(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {578 await usingApi(async (api) => {579 const tx = api.tx.nft.removeFromContractWhiteList(contractAddress.toString(), user.toString());580 const events = await submitTransactionAsync(sender, tx);581 const result = getGenericResult(events);582583 expect(result.success).to.be.true;584 });585}586587export async function removeFromContractWhiteListExpectFailure(sender: IKeyringPair, contractAddress: AccountId | string, user: AccountId | string) {588 await usingApi(async (api) => {589 const tx = api.tx.nft.removeFromContractWhiteList(contractAddress.toString(), user.toString());590 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;591 const result = getGenericResult(events);592593 expect(result.success).to.be.false;594 });595}596597export async function setVariableMetaDataExpectSuccess(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {598 await usingApi(async (api) => {599 const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));600 const events = await submitTransactionAsync(sender, tx);601 const result = getGenericResult(events);602603 expect(result.success).to.be.true;604 });605}606607export async function setVariableMetaDataExpectFailure(sender: IKeyringPair, collectionId: number, itemId: number, data: number[]) {608 await usingApi(async (api) => {609 const tx = api.tx.nft.setVariableMetaData(collectionId, itemId, '0x' + Buffer.from(data).toString('hex'));610 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;611 });612}613614export async function setOffchainSchemaExpectSuccess(sender: IKeyringPair, collectionId: number, data: number[]) {615 await usingApi(async (api) => {616 const tx = api.tx.nft.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));617 const events = await submitTransactionAsync(sender, tx);618 const result = getGenericResult(events);619620 expect(result.success).to.be.true;621 });622}623624export async function setOffchainSchemaExpectFailure(sender: IKeyringPair, collectionId: number, data: number[]) {625 await usingApi(async (api) => {626 const tx = api.tx.nft.setOffchainSchema(collectionId, '0x' + Buffer.from(data).toString('hex'));627 await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;628 });629}630631export interface CreateFungibleData {632 readonly Value: bigint;633}634635export interface CreateReFungibleData { }636export interface CreateNftData { }637638export type CreateItemData = {639 NFT: CreateNftData;640} | {641 Fungible: CreateFungibleData;642} | {643 ReFungible: CreateReFungibleData;644};645646export async function burnItemExpectSuccess(owner: IKeyringPair, collectionId: number, tokenId: number, value = 0) {647 await usingApi(async (api) => {648 const tx = api.tx.nft.burnItem(collectionId, tokenId, value);649 const events = await submitTransactionAsync(owner, tx);650 const result = getGenericResult(events);651 652 const item: any = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON();653 654 655 expect(result.success).to.be.true;656 657 expect(item).to.be.null;658 });659}660661export async function662 approveExpectSuccess(collectionId: number,663 tokenId: number, owner: IKeyringPair, approved: IKeyringPair | CrossAccountId | string, amount: number | bigint = 1) {664 await usingApi(async (api: ApiPromise) => {665 approved = normalizeAccountId(approved);666 const allowanceBefore =667 await api.query.nft.allowances(collectionId, [tokenId, owner.address, toSubstrateAddress(approved)]) as unknown as BN;668 const approveNftTx = api.tx.nft.approve(approved, collectionId, tokenId, amount);669 const events = await submitTransactionAsync(owner, approveNftTx);670 const result = getCreateItemResult(events);671 672 expect(result.success).to.be.true;673 const allowanceAfter =674 await api.query.nft.allowances(collectionId, [tokenId, owner.address, toSubstrateAddress(approved)]) as unknown as BN;675 expect(allowanceAfter.sub(allowanceBefore).toString()).to.be.equal(amount.toString());676 });677}678679export async function680 transferFromExpectSuccess(collectionId: number,681 tokenId: number,682 accountApproved: IKeyringPair,683 accountFrom: IKeyringPair | CrossAccountId,684 accountTo: IKeyringPair | CrossAccountId,685 value: number | bigint = 1,686 type: string = 'NFT') {687 await usingApi(async (api: ApiPromise) => {688 const to = normalizeAccountId(accountTo);689 let balanceBefore = new BN(0);690 if (type === 'Fungible') {691 balanceBefore = await api.query.nft.balance(collectionId, toSubstrateAddress(to)) as unknown as BN;692 }693 const transferFromTx = api.tx.nft.transferFrom(694 normalizeAccountId(accountFrom), to, collectionId, tokenId, value);695 const events = await submitTransactionAsync(accountApproved, transferFromTx);696 const result = getCreateItemResult(events);697 698 expect(result.success).to.be.true;699 if (type === 'NFT') {700 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId) as any).toJSON() as ITokenDataType;701 expect(nftItemData.Owner).to.be.deep.equal(to);702 }703 if (type === 'Fungible') {704 const balanceAfter = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to)) as any).Value as unknown as BN;705 expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(value.toString());706 }707 if (type === 'ReFungible') {708 const nftItemData =709 (await api.query.nft.reFungibleItemList(collectionId, tokenId) as any).toJSON() as IReFungibleTokenDataType;710 expect(nftItemData.Owner[0].Owner).to.be.deep.equal(normalizeAccountId(to));711 expect(nftItemData.Owner[0].Fraction).to.be.equal(value);712 }713 });714}715716export async function717 transferFromExpectFail(collectionId: number,718 tokenId: number,719 accountApproved: IKeyringPair,720 accountFrom: IKeyringPair,721 accountTo: IKeyringPair,722 value: number | bigint = 1) {723 await usingApi(async (api: ApiPromise) => {724 const transferFromTx = api.tx.nft.transferFrom(725 normalizeAccountId(accountFrom.address), normalizeAccountId(accountTo.address), collectionId, tokenId, value);726 const events = await expect(submitTransactionExpectFailAsync(accountApproved, transferFromTx)).to.be.rejected;727 const result = getCreateCollectionResult(events);728 729 expect(result.success).to.be.false;730 });731}732733async function getBlockNumber(api: ApiPromise): Promise<number> {734 return new Promise<number>(async (resolve, reject) => {735 const unsubscribe = await api.rpc.chain.subscribeNewHeads((head) => {736 unsubscribe();737 resolve(head.number.toNumber());738 });739 });740}741742export async function743scheduleTransferExpectSuccess(collectionId: number,744 tokenId: number,745 sender: IKeyringPair,746 recipient: IKeyringPair,747 value: number | bigint = 1,748 type: string = 'NFT') {749 await usingApi(async (api: ApiPromise) => {750 let balanceBefore = new BN(0);751752753 let blockNumber: number | undefined = await getBlockNumber(api);754 let expectedBlockNumber = blockNumber + 2;755756 expect(blockNumber).to.be.greaterThan(0);757 const transferTx = await api.tx.nft.transfer(recipient.address, collectionId, tokenId, value); 758 const scheduleTx = await api.tx.scheduler.schedule(expectedBlockNumber, null, 0, transferTx);759760 const events = await submitTransactionAsync(sender, scheduleTx);761762 const sponsorBalanceBefore = new BigNumber((await api.query.system.account(sender.address)).data.free.toString());763 const recipientBalanceBefore = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());764765 const nftItemDataBefore = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;766 expect(nftItemDataBefore.Owner.toString()).to.be.equal(sender.address);767768 769 await new Promise(resolve => setTimeout(resolve, 6000 * 2));770771 const sponsorBalanceAfter = new BigNumber((await api.query.system.account(sender.address)).data.free.toString());772 const recipientBalanceAfter = new BigNumber((await api.query.system.account(recipient.address)).data.free.toString());773774 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;775 expect(nftItemData.Owner.toString()).to.be.equal(recipient.address);776 expect(recipientBalanceAfter.toNumber()).to.be.equal(recipientBalanceBefore.toNumber());777 });778}779780781export async function782 transferExpectSuccess(collectionId: number,783 tokenId: number,784 sender: IKeyringPair,785 recipient: IKeyringPair | CrossAccountId,786 value: number | bigint = 1,787 type: string = 'NFT') {788 await usingApi(async (api: ApiPromise) => {789 const to = normalizeAccountId(recipient);790791 let balanceBefore = new BN(0);792 if (type === 'Fungible') {793 balanceBefore = await api.query.nft.balance(collectionId, toSubstrateAddress(to)) as unknown as BN;794 }795 const transferTx = api.tx.nft.transfer(to, collectionId, tokenId, value);796 const events = await submitTransactionAsync(sender, transferTx);797 const result = getTransferResult(events);798 799 expect(result.success).to.be.true;800 expect(result.collectionId).to.be.equal(collectionId);801 expect(result.itemId).to.be.equal(tokenId);802 expect(result.sender).to.be.deep.equal(normalizeAccountId(sender.address));803 expect(result.recipient).to.be.deep.equal(to);804 expect(result.value.toString()).to.be.equal(value.toString());805 if (type === 'NFT') {806 const nftItemData = (await api.query.nft.nftItemList(collectionId, tokenId)).toJSON() as unknown as ITokenDataType;807 expect(nftItemData.Owner).to.be.deep.equal(to);808 }809 if (type === 'Fungible') {810 const balanceAfter = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to)) as any).Value as unknown as BN;811 expect(balanceAfter.sub(balanceBefore).toString()).to.be.equal(value.toString());812 }813 if (type === 'ReFungible') {814 const nftItemData =815 (await api.query.nft.reFungibleItemList(collectionId, tokenId)).toJSON() as unknown as IReFungibleTokenDataType;816 expect(nftItemData.Owner[0].Owner).to.be.deep.equal(to);817 expect(nftItemData.Owner[0].Fraction.toString()).to.be.equal(value.toString());818 }819 });820}821822export async function823 transferExpectFail(collectionId: number,824 tokenId: number,825 sender: IKeyringPair,826 recipient: IKeyringPair,827 value: number | bigint = 1,828 type: string = 'NFT') {829 await usingApi(async (api: ApiPromise) => {830 const transferTx = api.tx.nft.transfer(normalizeAccountId(recipient.address), collectionId, tokenId, value);831 const events = await expect(submitTransactionExpectFailAsync(sender, transferTx)).to.be.rejected;832 if (events && Array.isArray(events)) {833 const result = getCreateCollectionResult(events);834 835 expect(result.success).to.be.false;836 }837 });838}839840export async function841 approveExpectFail(collectionId: number,842 tokenId: number, owner: IKeyringPair, approved: IKeyringPair, amount: number | bigint = 1) {843 await usingApi(async (api: ApiPromise) => {844 const approveNftTx = api.tx.nft.approve(normalizeAccountId(approved.address), collectionId, tokenId, amount);845 const events = await expect(submitTransactionExpectFailAsync(owner, approveNftTx)).to.be.rejected;846 const result = getCreateCollectionResult(events);847 848 expect(result.success).to.be.false;849 });850}851852export async function getFungibleBalance(853 collectionId: number,854 owner: string,855) {856 return await usingApi(async (api) => {857 const response = (await api.query.nft.fungibleItemList(collectionId, owner)).toJSON() as unknown as { Value: string };858 return BigInt(response.Value);859 });860}861862export async function createFungibleItemExpectSuccess(863 sender: IKeyringPair,864 collectionId: number,865 data: CreateFungibleData,866 owner: CrossAccountId | string = sender.address,867) {868 return await usingApi(async (api) => {869 const tx = api.tx.nft.createItem(collectionId, normalizeAccountId(owner), { Fungible: data });870871 const events = await submitTransactionAsync(sender, tx);872 const result = getCreateItemResult(events);873874 expect(result.success).to.be.true;875 return result.itemId;876 });877}878879export async function createItemExpectSuccess(880 sender: IKeyringPair, collectionId: number, createMode: string, owner: CrossAccountId | string = sender.address) {881 let newItemId: number = 0;882 await usingApi(async (api) => {883 const to = normalizeAccountId(owner);884 const AItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);885 const Aitem: any = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to))).toJSON();886 const AItemBalance = new BigNumber(Aitem.Value);887888 let tx;889 if (createMode === 'Fungible') {890 const createData = { fungible: { value: 10 } };891 tx = api.tx.nft.createItem(collectionId, to, createData);892 } else if (createMode === 'ReFungible') {893 const createData = { refungible: { const_data: [], variable_data: [], pieces: 100 } };894 tx = api.tx.nft.createItem(collectionId, to, createData);895 } else {896 tx = api.tx.nft.createItem(collectionId, to, createMode);897 }898899 const events = await submitTransactionAsync(sender, tx);900 const result = getCreateItemResult(events);901902 const BItemCount = parseInt((await api.query.nft.itemListIndex(collectionId)).toString(), 10);903 const Bitem: any = (await api.query.nft.fungibleItemList(collectionId, toSubstrateAddress(to))).toJSON();904 const BItemBalance = new BigNumber(Bitem.Value);905906 907 908 expect(result.success).to.be.true;909 if (createMode === 'Fungible') {910 expect(BItemBalance.minus(AItemBalance).toNumber()).to.be.equal(10);911 } else {912 expect(BItemCount).to.be.equal(AItemCount + 1);913 }914 expect(collectionId).to.be.equal(result.collectionId);915 expect(BItemCount.toString()).to.be.equal(result.itemId.toString());916 expect(to).to.be.deep.equal(result.recipient);917 newItemId = result.itemId;918 });919 return newItemId;920}921922export async function createItemExpectFailure(923 sender: IKeyringPair, collectionId: number, createMode: string, owner: string = sender.address) {924 await usingApi(async (api) => {925 const tx = api.tx.nft.createItem(collectionId, normalizeAccountId(owner), createMode);926 927 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;928 const result = getCreateItemResult(events);929930 expect(result.success).to.be.false;931 });932}933934export async function setPublicAccessModeExpectSuccess(935 sender: IKeyringPair, collectionId: number,936 accessMode: 'Normal' | 'WhiteList',937) {938 await usingApi(async (api) => {939940 941 const tx = api.tx.nft.setPublicAccessMode(collectionId, accessMode);942 const events = await submitTransactionAsync(sender, tx);943 const result = getGenericResult(events);944945 946 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();947948 949 950 expect(result.success).to.be.true;951 expect(collection.Access).to.be.equal(accessMode);952 });953}954955export async function enableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {956 await setPublicAccessModeExpectSuccess(sender, collectionId, 'WhiteList');957}958959export async function disableWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number) {960 await setPublicAccessModeExpectSuccess(sender, collectionId, 'Normal');961}962963export async function setMintPermissionExpectSuccess(sender: IKeyringPair, collectionId: number, enabled: boolean) {964 await usingApi(async (api) => {965966 967 const tx = api.tx.nft.setMintPermission(collectionId, enabled);968 const events = await submitTransactionAsync(sender, tx);969 const result = getGenericResult(events);970971 972 const collection: any = (await api.query.nft.collectionById(collectionId)).toJSON();973974 975 976 expect(result.success).to.be.true;977 expect(collection.MintMode).to.be.equal(enabled);978 });979}980981export async function enablePublicMintingExpectSuccess(sender: IKeyringPair, collectionId: number) {982 await setMintPermissionExpectSuccess(sender, collectionId, true);983}984985export async function setMintPermissionExpectFailure(sender: IKeyringPair, collectionId: number, enabled: boolean) {986 await usingApi(async (api) => {987 988 const tx = api.tx.nft.setMintPermission(collectionId, enabled);989 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;990 const result = getCreateCollectionResult(events);991 992 expect(result.success).to.be.false;993 });994}995996export async function isWhitelisted(collectionId: number, address: string) {997 let whitelisted: boolean = false;998 await usingApi(async (api) => {999 whitelisted = (await api.query.nft.whiteList(collectionId, address)).toJSON() as unknown as boolean;1000 });1001 return whitelisted;1002}10031004export async function addToWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: string | AccountId) {1005 await usingApi(async (api) => {10061007 const whiteListedBefore = (await api.query.nft.whiteList(collectionId, address)).toJSON();10081009 1010 const tx = api.tx.nft.addToWhiteList(collectionId, normalizeAccountId(address));1011 const events = await submitTransactionAsync(sender, tx);1012 const result = getGenericResult(events);10131014 const whiteListedAfter = (await api.query.nft.whiteList(collectionId, address)).toJSON();10151016 1017 1018 expect(result.success).to.be.true;1019 1020 expect(whiteListedBefore).to.be.false;1021 1022 expect(whiteListedAfter).to.be.true;1023 });1024}10251026export async function removeFromWhiteListExpectSuccess(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1027 await usingApi(async (api) => {1028 1029 const tx = api.tx.nft.removeFromWhiteList(collectionId, normalizeAccountId(address));1030 const events = await submitTransactionAsync(sender, tx);1031 const result = getGenericResult(events);10321033 1034 1035 expect(result.success).to.be.true;1036 });1037}10381039export async function removeFromWhiteListExpectFailure(sender: IKeyringPair, collectionId: number, address: CrossAccountId) {1040 await usingApi(async (api) => {1041 1042 const tx = api.tx.nft.removeFromWhiteList(collectionId, normalizeAccountId(address));1043 const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected;1044 const result = getGenericResult(events);10451046 1047 1048 expect(result.success).to.be.false;1049 });1050}10511052export const getDetailedCollectionInfo = async (api: ApiPromise, collectionId: number)1053 : Promise<ICollectionInterface | null> => {1054 return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;1055};10561057export const getCreatedCollectionCount = async (api: ApiPromise): Promise<number> => {1058 1059 return (await api.query.nft.createdCollectionCount() as unknown as BN).toNumber();1060};10611062export async function queryCollectionExpectSuccess(collectionId: number): Promise<ICollectionInterface> {1063 return await usingApi(async (api) => {1064 return (await api.query.nft.collectionById(collectionId)).toJSON() as unknown as ICollectionInterface;1065 });1066}