1import {ApiPromise} from '@polkadot/api';2import {3 RmrkTraitsNftAccountIdOrCollectionNftTuple as NftOwner,4 RmrkTraitsPropertyPropertyInfo as Property,5 RmrkTraitsResourceResourceInfo as ResourceInfo,6} from '@polkadot/types/lookup';7import type {EventRecord} from '@polkadot/types/interfaces';8import type {GenericEventData} from '@polkadot/types';9import privateKey from '../../substrate/privateKey';10import {NftIdTuple, getChildren, getOwnedNfts, getCollectionProperties, getNftProperties, getResources} from './fetch';11import chaiAsPromised from 'chai-as-promised';12import chai from 'chai';13import {getApiConnection} from '../../substrate/substrate-api';14import {Context} from 'mocha';1516chai.use(chaiAsPromised);17const expect = chai.expect;1819interface TxResult<T> {20 success: boolean;21 successData: T | null;22}2324export enum Pallets {25 Inflation = 'inflation',26 RmrkCore = 'rmrkcore',27 RmrkEquip = 'rmrkequip',28 ReFungible = 'refungible',29 Fungible = 'fungible',30 NFT = 'nonfungible',31 Scheduler = 'scheduler',32 AppPromotion = 'apppromotion',33}3435let modulesNames: any;36export function getModuleNames(api: ApiPromise): string[] {37 if (typeof modulesNames === 'undefined')38 modulesNames = api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());39 return modulesNames;40}4142export async function missingRequiredPallets(requiredPallets: string[]): Promise<string[]> {43 const api = await getApiConnection();44 const pallets = getModuleNames(api);4546 return requiredPallets.filter(p => !pallets.includes(p));47}4849export async function requirePallets(mocha: Context, requiredPallets: string[]) {50 const missingPallets = await missingRequiredPallets(requiredPallets);5152 if (missingPallets.length > 0) {53 const skippingTestMsg = `\tSkipping test "${mocha.test?.title}".`;54 const missingPalletsMsg = `\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;55 const skipMsg = `${skippingTestMsg}\n${missingPalletsMsg}`;5657 console.error('\x1b[38:5:208m%s\x1b[0m', skipMsg);5859 mocha.skip();60 }61}6263export function makeNftOwner(api: ApiPromise, owner: string | NftIdTuple): NftOwner {64 const isNftSending = (typeof owner !== 'string');6566 if (isNftSending) {67 return api.createType('RmrkTraitsNftAccountIdOrCollectionNftTuple', {68 'CollectionAndNftTuple': owner,69 });70 } else {71 const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format;72 return api.createType('RmrkTraitsNftAccountIdOrCollectionNftTuple', {73 AccountId: privateKey(owner, Number(ss58Format)).address,74 });75 }76}7778export async function isNftOwnedBy(79 api: ApiPromise,80 owner: string | NftIdTuple,81 collectionId: number,82 nftId: number,83): Promise<boolean> {84 if (typeof owner === 'string') {85 return (await getOwnedNfts(api, owner, collectionId))86 .find(ownedNftId => {87 return ownedNftId === nftId;88 }) !== undefined;89 } else {90 return (await getChildren(api, owner[0], owner[1]))91 .find(child => {92 return collectionId === child.collectionId.toNumber()93 && nftId === child.nftId.toNumber();94 }) !== undefined;95 }96}9798export function isPropertyExists(99 key: string,100 value: string,101 props: Property[],102): boolean {103 let isPropFound = false;104 for (let i = 0; i < props.length && !isPropFound; i++) {105 const fetchedProp = props[i];106107 isPropFound = fetchedProp.key.eq(key)108 && fetchedProp.value.eq(value);109 }110111 return isPropFound;112}113114export async function isCollectionPropertyExists(115 api: ApiPromise,116 collectionId: number,117 key: string,118 value: string,119): Promise<boolean> {120 const fetchedProps = await getCollectionProperties(api, collectionId);121 return isPropertyExists(key, value, fetchedProps);122}123124export async function isNftPropertyExists(125 api: ApiPromise,126 collectionId: number,127 nftId: number,128 key: string,129 value: string,130): Promise<boolean> {131 const fetchedProps = await getNftProperties(api, collectionId, nftId);132 return isPropertyExists(key, value, fetchedProps);133}134135export async function isNftChildOfAnother(136 api: ApiPromise,137 collectionId: number,138 nftId: number,139 parentNft: NftIdTuple,140): Promise<boolean> {141 return (await getChildren(api, parentNft[0], parentNft[1]))142 .find((childNft) => {143 return childNft.collectionId.toNumber() === collectionId144 && childNft.nftId.toNumber() === nftId;145 }) !== undefined;146}147148export function isTxResultSuccess(events: EventRecord[]): boolean {149 let success = false;150151 events.forEach(({event: {data, method, section}}) => {152 if (method == 'ExtrinsicSuccess') {153 success = true;154 }155 });156157 return success;158}159160export async function expectTxFailure(expectedError: RegExp, promise: Promise<any>) {161 await expect(promise).to.be.rejectedWith(expectedError);162}163164export function extractTxResult<T>(165 events: EventRecord[],166 expectSection: string,167 expectMethod: string,168 extractAction: (data: any) => T,169): TxResult<T> {170 let success = false;171 let successData = null;172 events.forEach(({event: {data, method, section}}) => {173 if (method == 'ExtrinsicSuccess') {174 success = true;175 } else if ((expectSection == section) && (expectMethod == method)) {176 successData = extractAction(data);177 }178 });179 const result: TxResult<T> = {180 success,181 successData,182 };183 return result;184}185186export function extractRmrkCoreTxResult<T>(187 events: EventRecord[],188 expectMethod: string,189 extractAction: (data: GenericEventData) => T,190): TxResult<T> {191 return extractTxResult(events, 'rmrkCore', expectMethod, extractAction);192}193194export function extractRmrkEquipTxResult<T>(195 events: EventRecord[],196 expectMethod: string,197 extractAction: (data: GenericEventData) => T,198): TxResult<T> {199 return extractTxResult(events, 'rmrkEquip', expectMethod, extractAction);200}201202export async function findResourceById(203 api: ApiPromise,204 collectionId: number,205 nftId: number,206 resourceId: number,207): Promise<ResourceInfo> {208 const resources = await getResources(api, collectionId, nftId);209210 let resource = null;211212 for (let i = 0; i < resources.length; i++) {213 const res = resources[i];214215 if (res.id.eq(resourceId)) {216 resource = res;217 break;218 }219 }220221 return resource!;222}223224export async function getResourceById(225 api: ApiPromise,226 collectionId: number,227 nftId: number,228 resourceId: number,229): Promise<ResourceInfo> {230 const resource = await findResourceById(231 api,232 collectionId,233 nftId,234 resourceId,235 );236237 expect(resource !== null, 'Error: resource was not found').to.be.true;238239 return resource!;240}241242export function checkResourceStatus(243 resource: ResourceInfo,244 expectedStatus: 'pending' | 'added',245) {246 expect(resource.pending.isTrue, `Error: added resource should be ${expectedStatus}`)247 .to.be.equal(expectedStatus === 'pending');248}