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);45 await api.disconnect();4647 return requiredPallets.filter(p => !pallets.includes(p));48}4950export async function requirePallets(mocha: Context, requiredPallets: string[]) {51 const missingPallets = await missingRequiredPallets(requiredPallets);5253 if (missingPallets.length > 0) {54 const skippingTestMsg = `\tSkipping test "${mocha.test?.title}".`;55 const missingPalletsMsg = `\tThe following pallets are missing:\n\t- ${missingPallets.join('\n\t- ')}`;56 const skipMsg = `${skippingTestMsg}\n${missingPalletsMsg}`;5758 console.error('\x1b[38:5:208m%s\x1b[0m', skipMsg);5960 mocha.skip();61 }62}6364export function makeNftOwner(api: ApiPromise, owner: string | NftIdTuple): NftOwner {65 const isNftSending = (typeof owner !== 'string');6667 if (isNftSending) {68 return api.createType('RmrkTraitsNftAccountIdOrCollectionNftTuple', {69 'CollectionAndNftTuple': owner,70 });71 } else {72 const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format;73 return api.createType('RmrkTraitsNftAccountIdOrCollectionNftTuple', {74 AccountId: privateKey(owner, Number(ss58Format)).address,75 });76 }77}7879export async function isNftOwnedBy(80 api: ApiPromise,81 owner: string | NftIdTuple,82 collectionId: number,83 nftId: number,84): Promise<boolean> {85 if (typeof owner === 'string') {86 return (await getOwnedNfts(api, owner, collectionId))87 .find(ownedNftId => {88 return ownedNftId === nftId;89 }) !== undefined;90 } else {91 return (await getChildren(api, owner[0], owner[1]))92 .find(child => {93 return collectionId === child.collectionId.toNumber()94 && nftId === child.nftId.toNumber();95 }) !== undefined;96 }97}9899export function isPropertyExists(100 key: string,101 value: string,102 props: Property[],103): boolean {104 let isPropFound = false;105 for (let i = 0; i < props.length && !isPropFound; i++) {106 const fetchedProp = props[i];107108 isPropFound = fetchedProp.key.eq(key)109 && fetchedProp.value.eq(value);110 }111112 return isPropFound;113}114115export async function isCollectionPropertyExists(116 api: ApiPromise,117 collectionId: number,118 key: string,119 value: string,120): Promise<boolean> {121 const fetchedProps = await getCollectionProperties(api, collectionId);122 return isPropertyExists(key, value, fetchedProps);123}124125export async function isNftPropertyExists(126 api: ApiPromise,127 collectionId: number,128 nftId: number,129 key: string,130 value: string,131): Promise<boolean> {132 const fetchedProps = await getNftProperties(api, collectionId, nftId);133 return isPropertyExists(key, value, fetchedProps);134}135136export async function isNftChildOfAnother(137 api: ApiPromise,138 collectionId: number,139 nftId: number,140 parentNft: NftIdTuple,141): Promise<boolean> {142 return (await getChildren(api, parentNft[0], parentNft[1]))143 .find((childNft) => {144 return childNft.collectionId.toNumber() === collectionId145 && childNft.nftId.toNumber() === nftId;146 }) !== undefined;147}148149export function isTxResultSuccess(events: EventRecord[]): boolean {150 let success = false;151152 events.forEach(({event: {method}}) => {153 if (method == 'ExtrinsicSuccess') {154 success = true;155 }156 });157158 return success;159}160161export async function expectTxFailure(expectedError: RegExp, promise: Promise<any>) {162 await expect(promise).to.be.rejectedWith(expectedError);163}164165export function extractTxResult<T>(166 events: EventRecord[],167 expectSection: string,168 expectMethod: string,169 extractAction: (data: any) => T,170): TxResult<T> {171 let success = false;172 let successData = null;173 events.forEach(({event: {data, method, section}}) => {174 if (method == 'ExtrinsicSuccess') {175 success = true;176 } else if ((expectSection == section) && (expectMethod == method)) {177 successData = extractAction(data);178 }179 });180 const result: TxResult<T> = {181 success,182 successData,183 };184 return result;185}186187export function extractRmrkCoreTxResult<T>(188 events: EventRecord[],189 expectMethod: string,190 extractAction: (data: GenericEventData) => T,191): TxResult<T> {192 return extractTxResult(events, 'rmrkCore', expectMethod, extractAction);193}194195export function extractRmrkEquipTxResult<T>(196 events: EventRecord[],197 expectMethod: string,198 extractAction: (data: GenericEventData) => T,199): TxResult<T> {200 return extractTxResult(events, 'rmrkEquip', expectMethod, extractAction);201}202203export async function findResourceById(204 api: ApiPromise,205 collectionId: number,206 nftId: number,207 resourceId: number,208): Promise<ResourceInfo> {209 const resources = await getResources(api, collectionId, nftId);210211 let resource = null;212213 for (let i = 0; i < resources.length; i++) {214 const res = resources[i];215216 if (res.id.eq(resourceId)) {217 resource = res;218 break;219 }220 }221222 return resource!;223}224225export async function getResourceById(226 api: ApiPromise,227 collectionId: number,228 nftId: number,229 resourceId: number,230): Promise<ResourceInfo> {231 const resource = await findResourceById(232 api,233 collectionId,234 nftId,235 resourceId,236 );237238 expect(resource !== null, 'Error: resource was not found').to.be.true;239240 return resource!;241}242243export function checkResourceStatus(244 resource: ResourceInfo,245 expectedStatus: 'pending' | 'added',246) {247 expect(resource.pending.isTrue, `Error: added resource should be ${expectedStatus}`)248 .to.be.equal(expectedStatus === 'pending');249}