12345678import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {SignerOptions} from '@polkadot/api/types/submittable';10import '../../interfaces/augment-api';11import {AugmentedSubmittables} from '@polkadot/api-base/types/submittable';12import {ApiInterfaceEvents} from '@polkadot/api/types';13import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';14import {IKeyringPair} from '@polkadot/types/types';15import {hexToU8a} from '@polkadot/util/hex';16import {u8aConcat} from '@polkadot/util/u8a';17import {18 IApiListeners,19 IBlock,20 IEvent,21 IChainProperties,22 ICollectionCreationOptions,23 ICollectionLimits,24 ICollectionPermissions,25 ICrossAccountId,26 ICrossAccountIdLower,27 ILogger,28 INestingPermissions,29 IProperty,30 IStakingInfo,31 ISchedulerOptions,32 ISubstrateBalance,33 IToken,34 ITokenPropertyPermission,35 ITransactionResult,36 IUniqueHelperLog,37 TApiAllowedListeners,38 TEthereumAccount,39 TSigner,40 TSubstrateAccount,41 TNetworks,42 IForeignAssetMetadata,43 AcalaAssetMetadata,44 MoonbeamAssetInfo,45 DemocracyStandardAccountVote,46 IEthCrossAccountId,47 CollectionFlag,48} from './types';49import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';50import type {Vec} from '@polkadot/types-codec';51import {FrameSystemEventRecord, PalletBalancesIdAmount} from '@polkadot/types/lookup';52import {arrayUnzip} from '@polkadot/util';5354export class CrossAccountId {55 Substrate!: TSubstrateAccount;56 Ethereum!: TEthereumAccount;5758 constructor(account: ICrossAccountId) {59 if('Substrate' in account) this.Substrate = account.Substrate;60 else this.Ethereum = account.Ethereum;61 }6263 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {64 switch (domain) {65 case 'Substrate': return new CrossAccountId({Substrate: account.address});66 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();67 }68 }6970 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {71 if('substrate' in address) return new CrossAccountId({Substrate: address.substrate});72 else return new CrossAccountId({Ethereum: address.ethereum});73 }7475 static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {76 return encodeAddress(decodeAddress(address), ss58Format);77 }7879 static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {80 return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});81 }8283 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {84 if(this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);85 return this;86 }8788 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {89 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));90 }9192 toEthereum(): CrossAccountId {93 if(this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});94 return this;95 }9697 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {98 return evmToAddress(address, ss58Format);99 }100101 toSubstrate(ss58Format?: number): CrossAccountId {102 if(this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});103 return this;104 }105106 toLowerCase(): CrossAccountId {107 if(this.Substrate) this.Substrate = this.Substrate.toLowerCase();108 if(this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();109 return this;110 }111}112113const nesting = {114 toChecksumAddress(address: string): string {115 if(typeof address === 'undefined') return '';116117 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);118119 address = address.toLowerCase().replace(/^0x/i, '');120 const addressHash = keccakAsHex(address).replace(/^0x/i, '');121 const checksumAddress = ['0x'];122123 for(let i = 0; i < address.length; i++) {124 125 if(parseInt(addressHash[i], 16) > 7) {126 checksumAddress.push(address[i].toUpperCase());127 } else {128 checksumAddress.push(address[i]);129 }130 }131 return checksumAddress.join('');132 },133 tokenIdToAddress(collectionId: number, tokenId: number) {134 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);135 },136};137138class UniqueUtil {139 static transactionStatus = {140 NOT_READY: 'NotReady',141 FAIL: 'Fail',142 SUCCESS: 'Success',143 };144145 static chainLogType = {146 EXTRINSIC: 'extrinsic',147 RPC: 'rpc',148 };149150 static getTokenAccount(token: IToken): CrossAccountId {151 return new CrossAccountId({Ethereum: this.getTokenAddress(token)});152 }153154 static getTokenAddress(token: IToken): string {155 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);156 }157158 static getDefaultLogger(): ILogger {159 return {160 log(msg: any, level = 'INFO') {161 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));162 },163 level: {164 ERROR: 'ERROR',165 WARNING: 'WARNING',166 INFO: 'INFO',167 },168 };169 }170171 static vec2str(arr: string[] | number[]) {172 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');173 }174175 static str2vec(string: string) {176 if(typeof string !== 'string') return string;177 return Array.from(string).map(x => x.charCodeAt(0));178 }179180 static fromSeed(seed: string, ss58Format = 42) {181 const keyring = new Keyring({type: 'sr25519', ss58Format});182 return keyring.addFromUri(seed);183 }184185 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {186 if(creationResult.status !== this.transactionStatus.SUCCESS) {187 throw Error('Unable to create collection!');188 }189190 let collectionId = null;191 creationResult.result.events.forEach(({event: {data, method, section}}) => {192 if((section === 'common') && (method === 'CollectionCreated')) {193 collectionId = parseInt(data[0].toString(), 10);194 }195 });196197 if(collectionId === null) {198 throw Error('No CollectionCreated event was found!');199 }200201 return collectionId;202 }203204 static extractTokensFromCreationResult(creationResult: ITransactionResult): {205 success: boolean,206 tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],207 } {208 if(creationResult.status !== this.transactionStatus.SUCCESS) {209 throw Error('Unable to create tokens!');210 }211 let success = false;212 const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];213 creationResult.result.events.forEach(({event: {data, method, section}}) => {214 if(method === 'ExtrinsicSuccess') {215 success = true;216 } else if((section === 'common') && (method === 'ItemCreated')) {217 tokens.push({218 collectionId: parseInt(data[0].toString(), 10),219 tokenId: parseInt(data[1].toString(), 10),220 owner: data[2].toHuman(),221 amount: data[3].toBigInt(),222 });223 }224 });225 return {success, tokens};226 }227228 static extractTokensFromBurnResult(burnResult: ITransactionResult): {229 success: boolean,230 tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],231 } {232 if(burnResult.status !== this.transactionStatus.SUCCESS) {233 throw Error('Unable to burn tokens!');234 }235 let success = false;236 const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];237 burnResult.result.events.forEach(({event: {data, method, section}}) => {238 if(method === 'ExtrinsicSuccess') {239 success = true;240 } else if((section === 'common') && (method === 'ItemDestroyed')) {241 tokens.push({242 collectionId: parseInt(data[0].toString(), 10),243 tokenId: parseInt(data[1].toString(), 10),244 owner: data[2].toHuman(),245 amount: data[3].toBigInt(),246 });247 }248 });249 return {success, tokens};250 }251252 static findCollectionInEvents(events: { event: IEvent }[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {253 let eventId = null;254 events.forEach(({event: {data, method, section}}) => {255 if((section === expectedSection) && (method === expectedMethod)) {256 eventId = parseInt(data[0].toString(), 10);257 }258 });259260 if(eventId === null) {261 throw Error(`No ${expectedMethod} event was found!`);262 }263 return eventId === collectionId;264 }265266 static isTokenTransferSuccess(events: { event: IEvent }[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {267 const normalizeAddress = (address: string | ICrossAccountId) => {268 if(typeof address === 'string') return address;269 const obj = {} as any;270 Object.keys(address).forEach(k => {271 obj[k.toLocaleLowerCase()] = (address as any)[k];272 });273 if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);274 if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();275 return address;276 };277 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;278 events.forEach(({event: {data, method, section}}) => {279 if((section === 'common') && (method === 'Transfer')) {280 const hData = (data as any).toJSON();281 transfer = {282 collectionId: hData[0],283 tokenId: hData[1],284 from: normalizeAddress(hData[2]),285 to: normalizeAddress(hData[3]),286 amount: BigInt(hData[4]),287 };288 }289 });290 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;291 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);292 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);293 isSuccess = isSuccess && amount === transfer.amount;294 return isSuccess;295 }296297 static bigIntToDecimals(number: bigint, decimals = 18) {298 const numberStr = number.toString();299 const dotPos = numberStr.length - decimals;300301 if(dotPos <= 0) {302 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;303 } else {304 const intPart = numberStr.substring(0, dotPos);305 const fractPart = numberStr.substring(dotPos);306 return intPart + '.' + fractPart;307 }308 }309}310311class UniqueEventHelper {312 private static extractIndex(index: any): [number, number] | string {313 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];314 return index.toJSON();315 }316317 private static extractSub(data: any, subTypes: any): { [key: string]: any } {318 let obj: any = {};319 let index = 0;320321 if(data.entries) {322 for(const [key, value] of data.entries()) {323 obj[key] = this.extractData(value, subTypes[index]);324 index++;325 }326 } else obj = data.toJSON();327328 return obj;329 }330331 private static toHuman(data: any) {332 return data && data.toHuman ? data.toHuman() : `${data}`;333 }334335 private static extractData(data: any, type: any): any {336 if(!type) return this.toHuman(data);337 if(['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();338 if(['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();339 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);340 return this.toHuman(data);341 }342343 public static extractEvents(events: { event: any, phase: any }[]): IEvent[] {344 const parsedEvents: IEvent[] = [];345346 events.forEach((record) => {347 const {event, phase} = record;348 const types = event.typeDef;349350 const eventData: IEvent = {351 section: event.section.toString(),352 method: event.method.toString(),353 index: this.extractIndex(event.index),354 data: [],355 phase: phase.toJSON(),356 };357358 event.data.forEach((val: any, index: number) => {359 eventData.data.push(this.extractData(val, types[index]));360 });361362 parsedEvents.push(eventData);363 });364365 return parsedEvents;366 }367}368const InvalidTypeSymbol = Symbol('Invalid type');369370export type Invalid<ErrorMessage> =371 | ((372 invalidType: typeof InvalidTypeSymbol,373 ..._: typeof InvalidTypeSymbol[]374 ) => typeof InvalidTypeSymbol)375 | null376 | undefined;377378type Get2<T, P extends string, E> =379 P extends `${infer Key}.${infer Key2}` ? Key extends keyof T ? Key2 extends keyof T[Key] ? T[Key][Key2] : E : E : E;380type ForceFunction<T> = T extends (...args: any) => any ? T : (...args: any) => Invalid<'not a function'>;381382export class ChainHelperBase {383 helperBase: any;384385 transactionStatus = UniqueUtil.transactionStatus;386 chainLogType = UniqueUtil.chainLogType;387 util: typeof UniqueUtil;388 eventHelper: typeof UniqueEventHelper;389 logger: ILogger;390 api: ApiPromise | null;391 forcedNetwork: TNetworks | null;392 network: TNetworks | null;393 wsEndpoint: string | null;394 chainLog: IUniqueHelperLog[];395 children: ChainHelperBase[];396 address: AddressGroup;397 chain: ChainGroup;398399 constructor(logger?: ILogger, helperBase?: any) {400 this.helperBase = helperBase;401402 this.util = UniqueUtil;403 this.eventHelper = UniqueEventHelper;404 if(typeof logger == 'undefined') logger = this.util.getDefaultLogger();405 this.logger = logger;406 this.api = null;407 this.forcedNetwork = null;408 this.network = null;409 this.wsEndpoint = null;410 this.chainLog = [];411 this.children = [];412 this.address = new AddressGroup(this);413 this.chain = new ChainGroup(this);414 }415416 clone(helperCls: ChainHelperBaseConstructor, options: { [key: string]: any } = {}) {417 Object.setPrototypeOf(helperCls.prototype, this);418 const newHelper = new helperCls(this.logger, options);419420 newHelper.api = this.api;421 newHelper.network = this.network;422 newHelper.forceNetwork = this.forceNetwork;423424 this.children.push(newHelper);425426 return newHelper;427 }428429 getEndpoint(): string {430 if(this.wsEndpoint === null) throw Error('No connection was established');431 return this.wsEndpoint;432 }433434 getApi(): ApiPromise {435 if(this.api === null) throw Error('API not initialized');436 return this.api;437 }438439 async subscribeEvents(expectedEvents: { section: string, names: string[] }[]) {440 const collectedEvents: IEvent[] = [];441 const unsubscribe = await this.getApi().query.system.events((events: Vec<FrameSystemEventRecord>) => {442 const ievents = this.eventHelper.extractEvents(events);443 ievents.forEach((event) => {444 expectedEvents.forEach((e => {445 if(event.section === e.section && e.names.includes(event.method)) {446 collectedEvents.push(event);447 }448 }));449 });450 });451 return {unsubscribe: unsubscribe as any, collectedEvents};452 }453454 clearChainLog(): void {455 this.chainLog = [];456 }457458 forceNetwork(value: TNetworks): void {459 this.forcedNetwork = value;460 }461462 async connect(wsEndpoint: string, listeners?: IApiListeners) {463 if(this.api !== null) throw Error('Already connected');464 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);465 this.wsEndpoint = wsEndpoint;466 this.api = api;467 this.network = network;468 }469470 async disconnect() {471 for(const child of this.children) {472 child.clearApi();473 }474475 if(this.api === null) return;476 await this.api.disconnect();477 this.clearApi();478 }479480 clearApi() {481 this.api = null;482 this.network = null;483 }484485 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {486 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;487 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];488489 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;490491 if(['quartz', 'unique', 'sapphire'].indexOf(spec.specName) > -1) return spec.specName;492 return 'opal';493 }494495 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {496 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});497 await api.isReady;498499 const network = await this.detectNetwork(api);500501 await api.disconnect();502503 return network;504 }505506 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{507 api: ApiPromise;508 network: TNetworks;509 }> {510 if(typeof network === 'undefined' || network === null) network = 'opal';511 const supportedRPC = {512 opal: {513 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,514 },515 quartz: {516 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,517 },518 unique: {519 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,520 },521 rococo: {},522 westend: {},523 moonbeam: {},524 moonriver: {},525 acala: {},526 karura: {},527 westmint: {},528 };529 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);530 const rpc = supportedRPC[network];531532 533 534535 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});536537 await api.isReadyOrError;538539 if(typeof listeners === 'undefined') listeners = {};540 for(const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {541 if(!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;542 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);543 }544545 return {api, network};546 }547548 getTransactionStatus(data: { events: { event: IEvent }[], status: any }) {549 const {events, status} = data;550 if(status.isReady) {551 return this.transactionStatus.NOT_READY;552 }553 if(status.isBroadcast) {554 return this.transactionStatus.NOT_READY;555 }556 if(status.isInBlock || status.isFinalized) {557 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');558 if(errors.length > 0) {559 return this.transactionStatus.FAIL;560 }561 if(events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {562 return this.transactionStatus.SUCCESS;563 }564 }565566 return this.transactionStatus.FAIL;567 }568569 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {570 const sign = (callback: any) => {571 if(options !== null) return transaction.signAndSend(sender, options, callback);572 return transaction.signAndSend(sender, callback);573 };574 575 return new Promise(async (resolve, reject) => {576 try {577 const unsub = await sign((result: any) => {578 const status = this.getTransactionStatus(result);579580 if(status === this.transactionStatus.SUCCESS) {581 this.logger.log(`${label} successful`);582 unsub();583 resolve({result, status, blockHash: result.status.asInBlock.toHuman()});584 } else if(status === this.transactionStatus.FAIL) {585 let moduleError = null;586587 if(result.hasOwnProperty('dispatchError')) {588 const dispatchError = result['dispatchError'];589590 if(dispatchError) {591 if(dispatchError.isModule) {592 const modErr = dispatchError.asModule;593 const errorMeta = dispatchError.registry.findMetaError(modErr);594595 moduleError = `${errorMeta.section}.${errorMeta.name}`;596 } else if(dispatchError.isToken) {597 moduleError = `Token: ${dispatchError.asToken}`;598 } else {599 600 moduleError = `Misc: ${dispatchError.toHuman()}`;601 }602 } else {603 this.logger.log(result, this.logger.level.ERROR);604 }605 }606607 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);608 unsub();609 reject({status, moduleError, result});610 }611 });612 } catch (e) {613 this.logger.log(e, this.logger.level.ERROR);614 reject(e);615 }616 });617 }618619 async signTransactionWithoutSending(signer: TSigner, tx: any) {620 const api = this.getApi();621 const signingInfo = await api.derive.tx.signingInfo(signer.address);622623 tx.sign(signer, {624 blockHash: api.genesisHash,625 genesisHash: api.genesisHash,626 runtimeVersion: api.runtimeVersion,627 nonce: signingInfo.nonce,628 });629630 return tx.toHex();631 }632633 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {634 const api = this.getApi();635 const signingInfo = await api.derive.tx.signingInfo(signer.address);636637 638 639 tx.sign(signer, {640 blockHash: api.genesisHash,641 genesisHash: api.genesisHash,642 runtimeVersion: api.runtimeVersion,643 nonce: signingInfo.nonce,644 });645646 if(len === null) {647 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;648 } else {649 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;650 }651 }652653 constructApiCall(apiCall: string, params: any[]) {654 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);655 let call = this.getApi() as any;656 for(const part of apiCall.slice(4).split('.')) {657 call = call[part];658 if(!call) {659 const advice = part.includes('_') ? ' Looks like it needs to be converted to camel case.' : '';660 throw Error(`Function ${part} of api call ${apiCall} not found.${advice}`);661 }662 }663 return call(...params);664 }665666 encodeApiCall(apiCall: string, params: any[]) {667 return this.constructApiCall(apiCall, params).method.toHex();668 }669670 async executeExtrinsic<671 E extends string,672 V extends (673 ...args: any) => any = ForceFunction<674 Get2<675 AugmentedSubmittables<'promise'>,676 E, (...args: any) => Invalid<'not found'>677 >678 >679 >(680 sender: TSigner,681 extrinsic: `api.tx.${E}`,682 params: Parameters<V>,683 expectSuccess = true,684 options: Partial<SignerOptions> | null = null,685 ): Promise<ITransactionResult> {686 if(this.api === null) throw Error('API not initialized');687688 const startTime = (new Date()).getTime();689 let result: ITransactionResult;690 let events: IEvent[] = [];691 try {692 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;693 events = this.eventHelper.extractEvents(result.result.events);694 const errorEvent = events.find((event) => event.method == 'ExecutedFailed' || event.method == 'CreatedFailed');695 if(errorEvent)696 throw Error(errorEvent.method + ': ' + extrinsic);697 }698 catch (e) {699 if(!(e as object).hasOwnProperty('status')) throw e;700 result = e as ITransactionResult;701 }702703 const endTime = (new Date()).getTime();704705 const log = {706 executedAt: endTime,707 executionTime: endTime - startTime,708 type: this.chainLogType.EXTRINSIC,709 status: result.status,710 call: extrinsic,711 signer: this.getSignerAddress(sender),712 params,713 } as IUniqueHelperLog;714715 let errorMessage = '';716717 if(result.status !== this.transactionStatus.SUCCESS) {718 if(result.moduleError) {719 errorMessage = typeof result.moduleError === 'string'720 ? result.moduleError721 : `${Object.keys(result.moduleError)[0]}: ${Object.values(result.moduleError)[0]}`;722 log.moduleError = errorMessage;723 }724 else if(result.result.dispatchError) log.dispatchError = result.result.dispatchError;725 }726 if(events.length > 0) log.events = events;727728 this.chainLog.push(log);729730 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {731 if(result.moduleError) throw Error(`${errorMessage}`);732 else if(result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));733 }734 return result as any;735 }736737 async callRpc738 739 740 741 742 743 744 745 746 747 748 749 750 (rpc: string, params?: any[]): Promise<any> {751752 if(typeof params === 'undefined') params = [] as any;753 if(this.api === null) throw Error('API not initialized');754 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);755756 const startTime = (new Date()).getTime();757 let result;758 let error = null;759 const log = {760 type: this.chainLogType.RPC,761 call: rpc,762 params,763 } as any as IUniqueHelperLog;764765 try {766 result = await this.constructApiCall(rpc, params as any);767 }768 catch (e) {769 error = e;770 }771772 const endTime = (new Date()).getTime();773774 log.executedAt = endTime;775 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';776 log.executionTime = endTime - startTime;777778 this.chainLog.push(log);779780 if(error !== null) throw error;781782 return result;783 }784785 getSignerAddress(signer: IKeyringPair | string): string {786 if(typeof signer === 'string') return signer;787 return signer.address;788 }789790 fetchAllPalletNames(): string[] {791 if(this.api === null) throw Error('API not initialized');792 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()).sort();793 }794795 fetchMissingPalletNames(requiredPallets: readonly string[]): string[] {796 const palletNames = this.fetchAllPalletNames();797 return requiredPallets.filter(p => !palletNames.includes(p));798 }799}800801802class HelperGroup<T extends ChainHelperBase> {803 helper: T;804805 constructor(uniqueHelper: T) {806 this.helper = uniqueHelper;807 }808}809810811class CollectionGroup extends HelperGroup<UniqueHelper> {812 813814815816817818819820821 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {822 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();823 }824825 826827828829830 async getTotalCount(): Promise<number> {831 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();832 }833834 835836837838839840841842843 async getData(collectionId: number): Promise<{844 id: number;845 name: string;846 description: string;847 tokensCount: number;848 admins: CrossAccountId[];849 normalizedOwner: TSubstrateAccount;850 raw: any851 } | null> {852 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);853 const humanCollection = collection.toHuman(), collectionData = {854 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],855 raw: humanCollection,856 } as any, jsonCollection = collection.toJSON();857 if(humanCollection === null) return null;858 collectionData.raw.limits = jsonCollection.limits;859 collectionData.raw.permissions = jsonCollection.permissions;860 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);861 for(const key of ['name', 'description']) {862 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);863 }864865 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))866 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)867 : 0;868 collectionData.admins = await this.getAdmins(collectionId);869870 return collectionData;871 }872873 874875876877878879880881 async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {882 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();883884 return normalize885 ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())886 : admins;887 }888889 890891892893894895896 async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {897 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();898 return normalize899 ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())900 : allowListed;901 }902903 904905906907908909910 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {911 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();912 }913914 915916917918919920921922 async burn(signer: TSigner, collectionId: number): Promise<boolean> {923 const result = await this.helper.executeExtrinsic(924 signer,925 'api.tx.unique.destroyCollection', [collectionId],926 true,927 );928929 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');930 }931932 933934935936937938939940941 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {942 const result = await this.helper.executeExtrinsic(943 signer,944 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],945 true,946 );947948 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');949 }950951 952953954955956957958959 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {960 const result = await this.helper.executeExtrinsic(961 signer,962 'api.tx.unique.confirmSponsorship', [collectionId],963 true,964 );965966 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');967 }968969 970971972973974975976977 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {978 const result = await this.helper.executeExtrinsic(979 signer,980 'api.tx.unique.removeCollectionSponsor', [collectionId],981 true,982 );983984 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');985 }986987 98898999099199299399499599699799899910001001100210031004 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {1005 const result = await this.helper.executeExtrinsic(1006 signer,1007 'api.tx.unique.setCollectionLimits', [collectionId, limits],1008 true,1009 );10101011 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');1012 }10131014 101510161017101810191020102110221023 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {1024 const result = await this.helper.executeExtrinsic(1025 signer,1026 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],1027 true,1028 );10291030 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');1031 }10321033 103410351036103710381039104010411042 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {1043 const result = await this.helper.executeExtrinsic(1044 signer,1045 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],1046 true,1047 );10481049 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');1050 }10511052 105310541055105610571058105910601061 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {1062 const result = await this.helper.executeExtrinsic(1063 signer,1064 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],1065 true,1066 );10671068 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');1069 }10701071 10721073107410751076107710781079 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {1080 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();1081 }10821083 1084108510861087108810891090 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1091 const result = await this.helper.executeExtrinsic(1092 signer,1093 'api.tx.unique.addToAllowList', [collectionId, addressObj],1094 true,1095 );10961097 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');1098 }10991100 11011102110311041105110611071108 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1109 const result = await this.helper.executeExtrinsic(1110 signer,1111 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1112 true,1113 );11141115 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');1116 }11171118 111911201121112211231124112511261127 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1128 const result = await this.helper.executeExtrinsic(1129 signer,1130 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1131 true,1132 );11331134 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');1135 }11361137 113811391140114111421143114411451146 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1147 return await this.setPermissions(signer, collectionId, {nesting: permissions});1148 }11491150 11511152115311541155115611571158 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1159 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1160 }11611162 116311641165116611671168116911701171 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1172 const result = await this.helper.executeExtrinsic(1173 signer,1174 'api.tx.unique.setCollectionProperties', [collectionId, properties],1175 true,1176 );11771178 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1179 }11801181 11821183118411851186118711881189 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1190 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1191 }11921193 async getPropertiesConsumedSpace(collectionId: number): Promise<number> {1194 const api = this.helper.getApi();1195 const props = (await api.query.common.collectionProperties(collectionId)).toJSON();11961197 return (props! as any).consumedSpace;1198 }11991200 async getCollectionOptions(collectionId: number) {1201 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1202 }12031204 120512061207120812091210121112121213 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1214 const result = await this.helper.executeExtrinsic(1215 signer,1216 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1217 true,1218 );12191220 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1221 }12221223 12241225122612271228122912301231123212331234 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1235 const result = await this.helper.executeExtrinsic(1236 signer,1237 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1238 true, 1239 );12401241 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1242 }12431244 1245124612471248124912501251125212531254125512561257 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1258 const result = await this.helper.executeExtrinsic(1259 signer,1260 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1261 true, 1262 );1263 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1264 }12651266 12671268126912701271127212731274127512761277 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {1278 const burnResult = await this.helper.executeExtrinsic(1279 signer,1280 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1281 true, 1282 );1283 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1284 if(burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1285 return burnedTokens.success;1286 }12871288 12891290129112921293129412951296129712981299 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1300 const burnResult = await this.helper.executeExtrinsic(1301 signer,1302 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1303 true, 1304 );1305 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1306 return burnedTokens.success && burnedTokens.tokens.length > 0;1307 }13081309 1310131113121313131413151316131713181319 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1320 const approveResult = await this.helper.executeExtrinsic(1321 signer,1322 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1323 true, 1324 );13251326 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1327 }13281329 13301331133213331334133513361337133813391340 async approveTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {1341 const approveResult = await this.helper.executeExtrinsic(1342 signer,1343 'api.tx.unique.approveFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1344 true, 1345 );13461347 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1348 }13491350 1351135213531354135513561357135813591360 async approveTokenFromEth(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1361 const ethMirror = CrossAccountId.fromKeyring(signer).toEthereum();1362 return await this.approveTokenFrom(signer, collectionId, tokenId, ethMirror, toAddressObj, amount);1363 }13641365 1366136713681369137013711372137313741375 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1376 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1377 }13781379 1380138113821383138413851386 async getLastTokenId(collectionId: number): Promise<number> {1387 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1388 }13891390 13911392139313941395139613971398 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1399 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1400 }1401}14021403class NFTnRFT extends CollectionGroup {1404 14051406140714081409141014111412 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1413 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1414 }14151416 1417141814191420142114221423142414251426 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1427 properties: IProperty[];1428 owner: CrossAccountId;1429 normalizedOwner: CrossAccountId;1430 } | null> {1431 let tokenData;1432 if(typeof blockHashAt === 'undefined') {1433 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1434 }1435 else {1436 if(propertyKeys.length == 0) {1437 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1438 if(!collection) return null;1439 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1440 }1441 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1442 }1443 tokenData = tokenData.toHuman();1444 if(tokenData === null || tokenData.owner === null) return null;1445 const owner = {} as any;1446 for(const key of Object.keys(tokenData.owner)) {1447 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1448 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1449 : tokenData.owner[key];1450 }1451 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1452 return tokenData;1453 }14541455 14561457145814591460146114621463 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1464 let owner;1465 if(typeof blockHashAt === 'undefined') {1466 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1467 } else {1468 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1469 }1470 return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1471 }14721473 14741475147614771478147914801481 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1482 let owner;1483 if(typeof blockHashAt === 'undefined') {1484 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1485 } else {1486 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1487 }14881489 if(owner === null) return null;14901491 return owner.toHuman();1492 }14931494 14951496149714981499150015011502 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1503 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1504 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1505 if(!result) {1506 throw Error('Unable to nest token!');1507 }1508 return result;1509 }15101511 151215131514151515161517151815191520 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1521 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1522 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1523 if(!result) {1524 throw Error('Unable to unnest token!');1525 }1526 return result;1527 }15281529 15301531153215331534153515361537153815391540 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1541 const result = await this.helper.executeExtrinsic(1542 signer,1543 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1544 true,1545 );15461547 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1548 }15491550 15511552155315541555155615571558 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1559 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1560 }15611562 1563156415651566156715681569157015711572 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1573 const result = await this.helper.executeExtrinsic(1574 signer,1575 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1576 true,1577 );15781579 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1580 }15811582 158315841585158615871588158915901591 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1592 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1593 }15941595 159615971598159916001601160216031604 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1605 const result = await this.helper.executeExtrinsic(1606 signer,1607 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1608 true,1609 );16101611 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1612 }16131614 161516161617161816191620162116221623 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1624 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1625 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1626 for(const key of ['name', 'description', 'tokenPrefix']) {1627 if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);1628 }16291630 let flags = 0;1631 1632 if(collectionOptions.flags) {1633 for(let i = 0; i < collectionOptions.flags.length; i++){1634 const flag = collectionOptions.flags[i];1635 flags = flags | flag;1636 }1637 }1638 collectionOptions.flags = [flags];16391640 const creationResult = await this.helper.executeExtrinsic(1641 signer,1642 'api.tx.unique.createCollectionEx', [collectionOptions],1643 true, 1644 );1645 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1646 }16471648 getCollectionObject(_collectionId: number): any {1649 return null;1650 }16511652 getTokenObject(_collectionId: number, _tokenId: number): any {1653 return null;1654 }16551656 1657165816591660166116621663 async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1664 return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1665 }16661667 166816691670167116721673 async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1674 const result = await this.helper.executeExtrinsic(1675 signer,1676 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1677 true,1678 );1679 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1680 }1681}168216831684class NFTGroup extends NFTnRFT {1685 168616871688168916901691 getCollectionObject(collectionId: number): UniqueNFTCollection {1692 return new UniqueNFTCollection(collectionId, this.helper);1693 }16941695 1696169716981699170017011702 getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1703 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1704 }17051706 1707170817091710171117121713 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1714 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1715 }17161717 1718171917201721172217231724172517261727 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1728 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1729 }17301731 173217331734173517361737173817391740174117421743 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1744 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1745 }17461747 17481749175017511752175317541755 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1756 let children;1757 if(typeof blockHashAt === 'undefined') {1758 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1759 } else {1760 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1761 }17621763 return children.toJSON().map((x: any) => ({collectionId: x.collection, tokenId: x.token}));1764 }17651766 176717681769177017711772177317741775177617771778 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1779 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1780 }17811782 178317841785178617871788 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1789 const creationResult = await this.helper.executeExtrinsic(1790 signer,1791 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1792 NFT: {1793 properties: data.properties,1794 },1795 }],1796 true,1797 );1798 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1799 if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1800 if(createdTokens.tokens.length < 1) throw Error('No tokens minted');1801 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1802 }18031804 180518061807180818091810181118121813181418151816181718181819 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {1820 const creationResult = await this.helper.executeExtrinsic(1821 signer,1822 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1823 true,1824 );1825 const collection = this.getCollectionObject(collectionId);1826 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1827 }18281829 183018311832183318341835183618371838183918401841184218431844184518461847 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {1848 const rawTokens = [];1849 for(const token of tokens) {1850 const raw = {NFT: {properties: token.properties}};1851 rawTokens.push(raw);1852 }1853 const creationResult = await this.helper.executeExtrinsic(1854 signer,1855 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1856 true,1857 );1858 const collection = this.getCollectionObject(collectionId);1859 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1860 }18611862 1863186418651866186718681869187018711872 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1873 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1874 }1875}187618771878class RFTGroup extends NFTnRFT {1879 188018811882188318841885 getCollectionObject(collectionId: number): UniqueRFTCollection {1886 return new UniqueRFTCollection(collectionId, this.helper);1887 }18881889 1890189118921893189418951896 getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1897 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1898 }18991900 1901190219031904190519061907 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1908 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1909 }19101911 19121913191419151916191719181919 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1920 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1921 }19221923 1924192519261927192819291930193119321933 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1934 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1935 }19361937 19381939194019411942194319441945194619471948 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1949 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1950 }19511952 195319541955195619571958195919601961196219631964 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1965 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1966 }19671968 1969197019711972197319741975 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1976 const creationResult = await this.helper.executeExtrinsic(1977 signer,1978 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1979 ReFungible: {1980 pieces: data.pieces,1981 properties: data.properties,1982 },1983 }],1984 true,1985 );1986 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1987 if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1988 if(createdTokens.tokens.length < 1) throw Error('No tokens minted');1989 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1990 }19911992 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {1993 throw Error('Not implemented');1994 const creationResult = await this.helper.executeExtrinsic(1995 signer,1996 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1997 true, 1998 );1999 const collection = this.getCollectionObject(collectionId);2000 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));2001 }20022003 200420052006200720082009201020112012 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {2013 const rawTokens = [];2014 for(const token of tokens) {2015 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};2016 rawTokens.push(raw);2017 }2018 const creationResult = await this.helper.executeExtrinsic(2019 signer,2020 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2021 true,2022 );2023 const collection = this.getCollectionObject(collectionId);2024 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));2025 }20262027 202820292030203120322033203420352036 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {2037 return await super.burnToken(signer, collectionId, tokenId, amount);2038 }20392040 2041204220432044204520462047204820492050 async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {2051 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);2052 }20532054 20552056205720582059206020612062206320642065 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {2066 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);2067 }20682069 2070207120722073207420752076 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {2077 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();2078 }20792080 208120822083208420852086208720882089 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {2090 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);2091 const repartitionResult = await this.helper.executeExtrinsic(2092 signer,2093 'api.tx.unique.repartition', [collectionId, tokenId, amount],2094 true,2095 );2096 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');2097 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');2098 }2099}210021012102class FTGroup extends CollectionGroup {2103 210421052106210721082109 getCollectionObject(collectionId: number): UniqueFTCollection {2110 return new UniqueFTCollection(collectionId, this.helper);2111 }21122113 2114211521162117211821192120212121222123212421252126 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {2127 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 2128 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');2129 collectionOptions.mode = {fungible: decimalPoints};2130 for(const key of ['name', 'description', 'tokenPrefix']) {2131 if(typeof collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] === 'string') collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] = this.helper.util.str2vec(collectionOptions[key as 'name' | 'description' | 'tokenPrefix'] as string);2132 }2133 const creationResult = await this.helper.executeExtrinsic(2134 signer,2135 'api.tx.unique.createCollectionEx', [collectionOptions],2136 true,2137 );2138 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));2139 }21402141 214221432144214521462147214821492150 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {2151 const creationResult = await this.helper.executeExtrinsic(2152 signer,2153 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {2154 Fungible: {2155 value: amount,2156 },2157 }],2158 true, 2159 );2160 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2161 }21622163 21642165216621672168216921702171 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: { value: bigint }[], owner: ICrossAccountId): Promise<boolean> {2172 const rawTokens = [];2173 for(const token of tokens) {2174 const raw = {Fungible: {Value: token.value}};2175 rawTokens.push(raw);2176 }2177 const creationResult = await this.helper.executeExtrinsic(2178 signer,2179 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2180 true,2181 );2182 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2183 }21842185 218621872188218921902191 async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2192 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2193 }21942195 2196219721982199220022012202 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2203 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2204 }22052206 220722082209221022112212221322142215 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {2216 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2217 }22182219 2220222122222223222422252226222722282229 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {2230 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2231 }22322233 22342235223622372238223922402241 async burnTokens(signer: IKeyringPair, collectionId: number, amount = 1n): Promise<boolean> {2242 return await super.burnToken(signer, collectionId, 0, amount);2243 }22442245 224622472248224922502251225222532254 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {2255 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2256 }22572258 22592260226122622263 async getTotalPieces(collectionId: number): Promise<bigint> {2264 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2265 }22662267 2268226922702271227222732274227522762277 approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {2278 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2279 }22802281 2282228322842285228622872288 getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2289 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2290 }2291}229222932294class ChainGroup extends HelperGroup<ChainHelperBase> {2295 22962297229822992300 getChainProperties(): IChainProperties {2301 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2302 return {2303 ss58Format: properties.ss58Format.toJSON(),2304 tokenDecimals: properties.tokenDecimals.toJSON(),2305 tokenSymbol: properties.tokenSymbol.toJSON(),2306 };2307 }23082309 23102311231223132314 async getLatestBlockNumber(): Promise<number> {2315 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2316 }23172318 231923202321232223232324 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2325 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2326 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2327 return blockHash;2328 }23292330 2331 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2332 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2333 if(!blockHash) return null;2334 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2335 }23362337 2338233923402341 async getRelayBlockNumber(): Promise<bigint> {2342 const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber;2343 return BigInt(blockNumber);2344 }23452346 234723482349235023512352 async getNonce(address: TSubstrateAccount): Promise<number> {2353 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2354 }2355}23562357class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2358 235923602361236223632364 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2365 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2366 }23672368 23692370237123722373237423752376 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2377 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);23782379 let transfer = {from: null, to: null, amount: 0n} as any;2380 result.result.events.forEach(({event: {data, method, section}}) => {2381 if((section === 'balances') && (method === 'Transfer')) {2382 transfer = {2383 from: this.helper.address.normalizeSubstrate(data[0]),2384 to: this.helper.address.normalizeSubstrate(data[1]),2385 amount: BigInt(data[2]),2386 };2387 }2388 });2389 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2390 && this.helper.address.normalizeSubstrate(address) === transfer.to2391 && BigInt(amount) === transfer.amount;2392 return isSuccess;2393 }23942395 23962397239823992400 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2401 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2402 return {free: accountInfo.free.toBigInt(), frozen: accountInfo.frozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2403 }24042405 2406240724082409 async getTotalIssuance(): Promise<bigint> {2410 const total = (await this.helper.callRpc('api.query.balances.totalIssuance', []));2411 return total.toBigInt();2412 }24132414 async getLocked(address: TSubstrateAccount): Promise<{ id: string, amount: bigint, reason: string }[]> {2415 const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman();2416 return locks.map((lock: any) => ({id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons}));2417 }2418 async getFrozen(address: TSubstrateAccount): Promise<{ id: string, amount: bigint }[]> {2419 const locks = (await this.helper.api!.query.balances.freezes(address)) as unknown as Array<any>;2420 return locks.map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()}));2421 }2422}24232424class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2425 242624272428242924302431 async getEthereum(address: TEthereumAccount): Promise<bigint> {2432 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2433 }24342435 24362437243824392440244124422443 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2444 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);24452446 let transfer = {from: null, to: null, amount: 0n} as any;2447 result.result.events.forEach(({event: {data, method, section}}) => {2448 if((section === 'balances') && (method === 'Transfer')) {2449 transfer = {2450 from: data[0].toString(),2451 to: data[1].toString(),2452 amount: BigInt(data[2]),2453 };2454 }2455 });2456 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2457 && address === transfer.to2458 && BigInt(amount) === transfer.amount;2459 return isSuccess;2460 }2461}24622463class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2464 subBalanceGroup: SubstrateBalanceGroup<T>;2465 ethBalanceGroup: EthereumBalanceGroup<T>;24662467 constructor(helper: T) {2468 super(helper);2469 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2470 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2471 }24722473 getCollectionCreationPrice(): bigint {2474 return 2n * this.getOneTokenNominal();2475 }2476 24772478247924802481 getOneTokenNominal(): bigint {2482 const chainProperties = this.helper.chain.getChainProperties();2483 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2484 }24852486 248724882489249024912492 getSubstrate(address: TSubstrateAccount): Promise<bigint> {2493 return this.subBalanceGroup.getSubstrate(address);2494 }24952496 24972498249925002501 getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2502 return this.subBalanceGroup.getSubstrateFull(address);2503 }25042505 2506250725082509 getTotalIssuance(): Promise<bigint> {2510 return this.subBalanceGroup.getTotalIssuance();2511 }25122513 251425152516251725182519 getLocked(address: TSubstrateAccount) {2520 return this.subBalanceGroup.getLocked(address);2521 }25222523 25242525252625272528 getFrozen(address: TSubstrateAccount) {2529 return this.subBalanceGroup.getFrozen(address);2530 }25312532 253325342535253625372538 getEthereum(address: TEthereumAccount): Promise<bigint> {2539 return this.ethBalanceGroup.getEthereum(address);2540 }25412542 async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint) {2543 await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceSetBalance', [address, amount], true);2544 }25452546 25472548254925502551255225532554 transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2555 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2556 }25572558 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2559 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);25602561 let transfer = {from: null, to: null, amount: 0n} as any;2562 result.result.events.forEach(({event: {data, method, section}}) => {2563 if((section === 'balances') && (method === 'Transfer')) {2564 transfer = {2565 from: this.helper.address.normalizeSubstrate(data[0]),2566 to: this.helper.address.normalizeSubstrate(data[1]),2567 amount: BigInt(data[2]),2568 };2569 }2570 });2571 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2572 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2573 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2574 return isSuccess;2575 }25762577 2578257925802581258225832584 async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: { start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }): Promise<void> {2585 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, schedule]);2586 const event = result.result.events2587 .find(e => e.event.section === 'vesting' &&2588 e.event.method === 'VestingScheduleAdded' &&2589 e.event.data[0].toHuman() === signer.address);2590 if(!event) throw Error('Cannot find transfer in events');2591 }25922593 25942595259625972598 async getVestingSchedules(address: TSubstrateAccount): Promise<{ start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }[]> {2599 const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON();2600 return schedule.map((schedule: any) => ({2601 start: BigInt(schedule.start),2602 period: BigInt(schedule.period),2603 periodCount: BigInt(schedule.periodCount),2604 perPeriod: BigInt(schedule.perPeriod),2605 }));2606 }26072608 2609261026112612 async claim(signer: TSigner) {2613 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []);2614 const event = result.result.events2615 .find(e => e.event.section === 'vesting' &&2616 e.event.method === 'Claimed' &&2617 e.event.data[0].toHuman() === signer.address);2618 if(!event) throw Error('Cannot find claim in events');2619 }2620}26212622class AddressGroup extends HelperGroup<ChainHelperBase> {2623 2624262526262627262826292630 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2631 return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2632 }26332634 263526362637263826392640 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2641 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2642 }26432644 2645264626472648264926502651 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat = false): TSubstrateAccount {2652 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2653 }26542655 265626572658265926602661 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2662 return CrossAccountId.translateSubToEth(subAddress);2663 }26642665 266626672668266926702671 encodeSubstrateAddress(key: Uint8Array | string | bigint, ss58Format = 42): string {2672 const u8a: Uint8Array = typeof key === 'string'2673 ? hexToU8a(key)2674 : typeof key === 'bigint'2675 ? hexToU8a(key.toString(16))2676 : key;26772678 if(ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2679 throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2680 }26812682 const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2683 if(!allowedDecodedLengths.includes(u8a.length)) {2684 throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2685 }26862687 const u8aPrefix = ss58Format < 642688 ? new Uint8Array([ss58Format])2689 : new Uint8Array([2690 ((ss58Format & 0xfc) >> 2) | 0x40,2691 (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2692 ]);26932694 const input = u8aConcat(u8aPrefix, u8a);26952696 return base58Encode(u8aConcat(2697 input,2698 blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2699 ));2700 }27012702 27032704270527062707 restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2708 if(this.helper.api === null) {2709 throw 'Not connected';2710 }2711 const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2712 if(res === undefined || res === null) {2713 throw 'Restore address error';2714 }2715 return res.toString();2716 }27172718 27192720272127222723 convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2724 if(ethCrossAccount.sub === '0') {2725 return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2726 }27272728 const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2729 return {Substrate: ss58};2730 }27312732 paraSiblingSovereignAccount(paraid: number) {2733 2734 2735 const siblingPrefix = '0x7369626c';27362737 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2738 const suffix = '000000000000000000000000000000000000000000000000';27392740 return siblingPrefix + encodedParaId + suffix;2741 }2742}27432744class StakingGroup extends HelperGroup<UniqueHelper> {2745 2746274727482749275027512752 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2753 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2754 const _stakeResult = await this.helper.executeExtrinsic(2755 signer, 'api.tx.appPromotion.stake',2756 [amountToStake], true,2757 );2758 2759 return true;2760 }27612762 2763276427652766276727682769 async unstakeAll(signer: TSigner, label?: string): Promise<string> {2770 if(typeof label === 'undefined') label = `${signer.address}`;2771 const unstakeResult = await this.helper.executeExtrinsic(2772 signer, 'api.tx.appPromotion.unstakeAll',2773 [], true,2774 );2775 return unstakeResult.blockHash;2776 }27772778 2779278027812782278327842785 async unstakePartial(signer: TSigner, amount: bigint, label?: string): Promise<string> {2786 if(typeof label === 'undefined') label = `${signer.address}`;2787 const unstakeResult = await this.helper.executeExtrinsic(2788 signer, 'api.tx.appPromotion.unstakePartial',2789 [amount], true,2790 );2791 return unstakeResult.blockHash;2792 }27932794 27952796279727982799 async getStakesNumber(address: ICrossAccountId): Promise<number> {2800 if('Ethereum' in address) throw Error('only substrate address');2801 return (await this.helper.callRpc('api.query.appPromotion.stakesPerAccount', [address.Substrate])).toNumber();2802 }28032804 28052806280728082809 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2810 if(address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2811 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2812 }28132814 28152816281728182819 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2820 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2821 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => ({2822 block: block.toBigInt(),2823 amount: amount.toBigInt(),2824 }));2825 }28262827 28282829283028312832 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2833 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2834 }28352836 28372838283928402841 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2842 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2843 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => ({2844 block: block.toBigInt(),2845 amount: amount.toBigInt(),2846 }));2847 return result;2848 }2849}28502851class SchedulerGroup extends HelperGroup<UniqueHelper> {2852 constructor(helper: UniqueHelper) {2853 super(helper);2854 }28552856 cancelScheduled(signer: TSigner, scheduledId: string) {2857 return this.helper.executeExtrinsic(2858 signer,2859 'api.tx.scheduler.cancelNamed',2860 [scheduledId],2861 true,2862 );2863 }28642865 changePriority(signer: TSigner, scheduledId: string, priority: number) {2866 return this.helper.executeExtrinsic(2867 signer,2868 'api.tx.scheduler.changeNamedPriority',2869 [scheduledId, priority],2870 true,2871 );2872 }28732874 scheduleAt<T extends UniqueHelper>(2875 executionBlockNumber: number,2876 options: ISchedulerOptions = {},2877 ) {2878 return this.schedule<T>('schedule', executionBlockNumber, options);2879 }28802881 scheduleAfter<T extends UniqueHelper>(2882 blocksBeforeExecution: number,2883 options: ISchedulerOptions = {},2884 ) {2885 return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);2886 }28872888 schedule<T extends UniqueHelper>(2889 scheduleFn: 'schedule' | 'scheduleAfter',2890 blocksNum: number,2891 options: ISchedulerOptions = {},2892 ) {2893 2894 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2895 return this.helper.clone(ScheduledHelperType, {2896 scheduleFn,2897 blocksNum,2898 options,2899 }) as T;2900 }2901}29022903class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2904 2905 addInvulnerable(signer: TSigner, address: string) {2906 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);2907 }29082909 removeInvulnerable(signer: TSigner, address: string) {2910 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);2911 }29122913 async getInvulnerables(): Promise<string[]> {2914 return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());2915 }29162917 2918 maxCollators(): number {2919 return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);2920 }29212922 async getDesiredCollators(): Promise<number> {2923 return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();2924 }29252926 setLicenseBond(signer: TSigner, amount: bigint) {2927 return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);2928 }29292930 async getLicenseBond(): Promise<bigint> {2931 return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();2932 }29332934 obtainLicense(signer: TSigner) {2935 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);2936 }29372938 releaseLicense(signer: TSigner) {2939 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);2940 }29412942 forceReleaseLicense(signer: TSigner, released: string) {2943 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);2944 }29452946 async hasLicense(address: string): Promise<bigint> {2947 return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();2948 }29492950 onboard(signer: TSigner) {2951 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);2952 }29532954 offboard(signer: TSigner) {2955 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);2956 }29572958 async getCandidates(): Promise<string[]> {2959 return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());2960 }2961}29622963class PreimageGroup extends HelperGroup<UniqueHelper> {2964 async getPreimageInfo(h256: string) {2965 return (await this.helper.callRpc('api.query.preimage.statusFor', [h256])).toJSON();2966 }29672968 296929702971297229732974297529762977 notePreimage(signer: TSigner, bytes: string | Uint8Array) {2978 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.notePreimage', [bytes]);2979 }29802981 298229832984298529862987 unnotePreimage(signer: TSigner, h256: string) {2988 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unnotePreimage', [h256]);2989 }29902991 299229932994299529962997 requestPreimage(signer: TSigner, h256: string) {2998 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.requestPreimage', [h256]);2999 }30003001 300230033004300530063007 unrequestPreimage(signer: TSigner, h256: string) {3008 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unrequestPreimage', [h256]);3009 }3010}30113012class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {3013 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {3014 await this.helper.executeExtrinsic(3015 signer,3016 'api.tx.foreignAssets.registerForeignAsset',3017 [ownerAddress, location, metadata],3018 true,3019 );3020 }30213022 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {3023 await this.helper.executeExtrinsic(3024 signer,3025 'api.tx.foreignAssets.updateForeignAsset',3026 [foreignAssetId, location, metadata],3027 true,3028 );3029 }3030}30313032class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {3033 palletName: string;30343035 constructor(helper: T, palletName: string) {3036 super(helper);30373038 this.palletName = palletName;3039 }30403041 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {3042 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);3043 }30443045 async setSafeXcmVersion(signer: TSigner, version: number) {3046 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);3047 }30483049 async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {3050 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);3051 }30523053 async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {3054 const destinationContent = {3055 parents: 0,3056 interior: {3057 X1: {3058 Parachain: destinationParaId,3059 },3060 },3061 };30623063 const beneficiaryContent = {3064 parents: 0,3065 interior: {3066 X1: {3067 AccountId32: {3068 network: 'Any',3069 id: targetAccount,3070 },3071 },3072 },3073 };30743075 const assetsContent = [3076 {3077 id: {3078 Concrete: {3079 parents: 0,3080 interior: 'Here',3081 },3082 },3083 fun: {3084 Fungible: amount,3085 },3086 },3087 ];30883089 let destination;3090 let beneficiary;3091 let assets;30923093 if(xcmVersion == 2) {3094 destination = {V1: destinationContent};3095 beneficiary = {V1: beneficiaryContent};3096 assets = {V1: assetsContent};30973098 } else if(xcmVersion == 3) {3099 destination = {V2: destinationContent};3100 beneficiary = {V2: beneficiaryContent};3101 assets = {V2: assetsContent};31023103 } else {3104 throw Error('Unknown XCM version: ' + xcmVersion);3105 }31063107 const feeAssetItem = 0;31083109 await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);3110 }31113112 async send(signer: IKeyringPair, destination: any, message: any) {3113 await this.helper.executeExtrinsic(3114 signer,3115 `api.tx.${this.palletName}.send`,3116 [3117 destination,3118 message,3119 ],3120 true,3121 );3122 }3123}31243125class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {3126 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {3127 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);3128 }31293130 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {3131 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);3132 }31333134 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {3135 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);3136 }3137}31383139class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {3140 async accounts(address: string, currencyId: any) {3141 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;3142 return BigInt(free);3143 }3144}31453146class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {3147 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {3148 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);3149 }31503151 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {3152 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);3153 }31543155 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {3156 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);3157 }31583159 async account(assetId: string | number, address: string) {3160 const accountAsset = (3161 await this.helper.callRpc('api.query.assets.account', [assetId, address])3162 ).toJSON()! as any;31633164 if(accountAsset !== null) {3165 return BigInt(accountAsset['balance']);3166 } else {3167 return null;3168 }3169 }3170}31713172class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {3173 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {3174 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);3175 }3176}31773178class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {3179 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {3180 const apiPrefix = 'api.tx.assetManager.';31813182 const registerTx = this.helper.constructApiCall(3183 apiPrefix + 'registerForeignAsset',3184 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],3185 );31863187 const setUnitsTx = this.helper.constructApiCall(3188 apiPrefix + 'setAssetUnitsPerSecond',3189 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],3190 );31913192 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);3193 const encodedProposal = batchCall?.method.toHex() || '';3194 return encodedProposal;3195 }31963197 async assetTypeId(location: any) {3198 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);3199 }3200}32013202class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {3203 notePreimagePallet: string;32043205 constructor(helper: MoonbeamHelper, options: { [key: string]: any } = {}) {3206 super(helper);3207 this.notePreimagePallet = options.notePreimagePallet;3208 }32093210 async notePreimage(signer: TSigner, encodedProposal: string) {3211 await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);3212 }32133214 externalProposeMajority(proposal: any) {3215 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);3216 }32173218 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {3219 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);3220 }32213222 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {3223 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);3224 }3225}32263227class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {3228 collective: string;32293230 constructor(helper: MoonbeamHelper, collective: string) {3231 super(helper);32323233 this.collective = collective;3234 }32353236 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {3237 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);3238 }32393240 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {3241 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);3242 }32433244 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {3245 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);3246 }32473248 async proposalCount() {3249 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));3250 }3251}32523253export type ChainHelperBaseConstructor = new (...args: any[]) => ChainHelperBase;3254export type UniqueHelperConstructor = new (...args: any[]) => UniqueHelper;32553256export class UniqueHelper extends ChainHelperBase {3257 balance: BalanceGroup<UniqueHelper>;3258 collection: CollectionGroup;3259 nft: NFTGroup;3260 rft: RFTGroup;3261 ft: FTGroup;3262 staking: StakingGroup;3263 scheduler: SchedulerGroup;3264 collatorSelection: CollatorSelectionGroup;3265 preimage: PreimageGroup;3266 foreignAssets: ForeignAssetsGroup;3267 xcm: XcmGroup<UniqueHelper>;3268 xTokens: XTokensGroup<UniqueHelper>;3269 tokens: TokensGroup<UniqueHelper>;32703271 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3272 super(logger, options.helperBase ?? UniqueHelper);32733274 this.balance = new BalanceGroup(this);3275 this.collection = new CollectionGroup(this);3276 this.nft = new NFTGroup(this);3277 this.rft = new RFTGroup(this);3278 this.ft = new FTGroup(this);3279 this.staking = new StakingGroup(this);3280 this.scheduler = new SchedulerGroup(this);3281 this.collatorSelection = new CollatorSelectionGroup(this);3282 this.preimage = new PreimageGroup(this);3283 this.foreignAssets = new ForeignAssetsGroup(this);3284 this.xcm = new XcmGroup(this, 'polkadotXcm');3285 this.xTokens = new XTokensGroup(this);3286 this.tokens = new TokensGroup(this);3287 }32883289 getSudo<T extends UniqueHelper>() {3290 3291 const SudoHelperType = SudoHelper(this.helperBase);3292 return this.clone(SudoHelperType) as T;3293 }3294}32953296export class XcmChainHelper extends ChainHelperBase {3297 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {3298 const wsProvider = new WsProvider(wsEndpoint);3299 this.api = new ApiPromise({3300 provider: wsProvider,3301 });3302 await this.api.isReadyOrError;3303 this.network = await UniqueHelper.detectNetwork(this.api);3304 }3305}33063307export class RelayHelper extends XcmChainHelper {3308 balance: SubstrateBalanceGroup<RelayHelper>;3309 xcm: XcmGroup<RelayHelper>;33103311 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3312 super(logger, options.helperBase ?? RelayHelper);33133314 this.balance = new SubstrateBalanceGroup(this);3315 this.xcm = new XcmGroup(this, 'xcmPallet');3316 }3317}33183319export class WestmintHelper extends XcmChainHelper {3320 balance: SubstrateBalanceGroup<WestmintHelper>;3321 xcm: XcmGroup<WestmintHelper>;3322 assets: AssetsGroup<WestmintHelper>;3323 xTokens: XTokensGroup<WestmintHelper>;33243325 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3326 super(logger, options.helperBase ?? WestmintHelper);33273328 this.balance = new SubstrateBalanceGroup(this);3329 this.xcm = new XcmGroup(this, 'polkadotXcm');3330 this.assets = new AssetsGroup(this);3331 this.xTokens = new XTokensGroup(this);3332 }3333}33343335export class MoonbeamHelper extends XcmChainHelper {3336 balance: EthereumBalanceGroup<MoonbeamHelper>;3337 assetManager: MoonbeamAssetManagerGroup;3338 assets: AssetsGroup<MoonbeamHelper>;3339 xTokens: XTokensGroup<MoonbeamHelper>;3340 democracy: MoonbeamDemocracyGroup;3341 collective: {3342 council: MoonbeamCollectiveGroup,3343 techCommittee: MoonbeamCollectiveGroup,3344 };33453346 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3347 super(logger, options.helperBase ?? MoonbeamHelper);33483349 this.balance = new EthereumBalanceGroup(this);3350 this.assetManager = new MoonbeamAssetManagerGroup(this);3351 this.assets = new AssetsGroup(this);3352 this.xTokens = new XTokensGroup(this);3353 this.democracy = new MoonbeamDemocracyGroup(this, options);3354 this.collective = {3355 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),3356 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),3357 };3358 }3359}33603361export class AstarHelper extends XcmChainHelper {3362 balance: SubstrateBalanceGroup<AstarHelper>;3363 assets: AssetsGroup<AstarHelper>;3364 xcm: XcmGroup<AstarHelper>;33653366 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3367 super(logger, options.helperBase ?? AstarHelper);33683369 this.balance = new SubstrateBalanceGroup(this);3370 this.assets = new AssetsGroup(this);3371 this.xcm = new XcmGroup(this, 'polkadotXcm');3372 }33733374 getSudo<T extends UniqueHelper>() {3375 3376 const SudoHelperType = SudoHelper(this.helperBase);3377 return this.clone(SudoHelperType) as T;3378 }3379}33803381export class AcalaHelper extends XcmChainHelper {3382 balance: SubstrateBalanceGroup<AcalaHelper>;3383 assetRegistry: AcalaAssetRegistryGroup;3384 xTokens: XTokensGroup<AcalaHelper>;3385 tokens: TokensGroup<AcalaHelper>;3386 xcm: XcmGroup<AcalaHelper>;33873388 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3389 super(logger, options.helperBase ?? AcalaHelper);33903391 this.balance = new SubstrateBalanceGroup(this);3392 this.assetRegistry = new AcalaAssetRegistryGroup(this);3393 this.xTokens = new XTokensGroup(this);3394 this.tokens = new TokensGroup(this);3395 this.xcm = new XcmGroup(this, 'polkadotXcm');3396 }33973398 getSudo<T extends AcalaHelper>() {3399 3400 const SudoHelperType = SudoHelper(this.helperBase);3401 return this.clone(SudoHelperType) as T;3402 }3403}340434053406function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {3407 return class extends Base {3408 scheduleFn: 'schedule' | 'scheduleAfter';3409 blocksNum: number;3410 options: ISchedulerOptions;34113412 constructor(...args: any[]) {3413 const logger = args[0] as ILogger;3414 const options = args[1] as {3415 scheduleFn: 'schedule' | 'scheduleAfter',3416 blocksNum: number,3417 options: ISchedulerOptions3418 };34193420 super(logger);34213422 this.scheduleFn = options.scheduleFn;3423 this.blocksNum = options.blocksNum;3424 this.options = options.options;3425 }34263427 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {3428 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);34293430 const mandatorySchedArgs = [3431 this.blocksNum,3432 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,3433 this.options.priority ?? null,3434 scheduledTx,3435 ];34363437 let schedArgs;3438 let scheduleFn;34393440 if(this.options.scheduledId) {3441 schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];34423443 if(this.scheduleFn == 'schedule') {3444 scheduleFn = 'scheduleNamed';3445 } else if(this.scheduleFn == 'scheduleAfter') {3446 scheduleFn = 'scheduleNamedAfter';3447 }3448 } else {3449 schedArgs = mandatorySchedArgs;3450 scheduleFn = this.scheduleFn;3451 }34523453 const extrinsic = 'api.tx.scheduler.' + scheduleFn;34543455 return super.executeExtrinsic(3456 sender,3457 extrinsic as any,3458 schedArgs,3459 expectSuccess,3460 );3461 }3462 };3463}346434653466function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {3467 return class extends Base {3468 constructor(...args: any[]) {3469 super(...args);3470 }34713472 async executeExtrinsic(3473 sender: IKeyringPair,3474 extrinsic: string,3475 params: any[],3476 expectSuccess?: boolean,3477 options: Partial<SignerOptions> | null = null,3478 ): Promise<ITransactionResult> {3479 const call = this.constructApiCall(extrinsic, params);3480 const result = await super.executeExtrinsic(3481 sender,3482 'api.tx.sudo.sudo',3483 [call],3484 expectSuccess,3485 options,3486 );34873488 if(result.status === 'Fail') return result;34893490 const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;3491 if(data.isErr) {3492 if(data.asErr.isModule) {3493 const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;3494 const metaError = super.getApi()?.registry.findMetaError(error);3495 throw new Error(`${metaError.section}.${metaError.name}`);3496 } else if(data.asErr.isToken) {3497 throw new Error(`Token: ${data.asErr.asToken}`);3498 }3499 3500 throw new Error(`Misc: ${data.asErr.toHuman()}`);3501 }3502 return result;3503 }3504 };3505}35063507export class UniqueBaseCollection {3508 helper: UniqueHelper;3509 collectionId: number;35103511 constructor(collectionId: number, uniqueHelper: UniqueHelper) {3512 this.collectionId = collectionId;3513 this.helper = uniqueHelper;3514 }35153516 async getData() {3517 return await this.helper.collection.getData(this.collectionId);3518 }35193520 async getLastTokenId() {3521 return await this.helper.collection.getLastTokenId(this.collectionId);3522 }35233524 async doesTokenExist(tokenId: number) {3525 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);3526 }35273528 async getAdmins() {3529 return await this.helper.collection.getAdmins(this.collectionId);3530 }35313532 async getAllowList() {3533 return await this.helper.collection.getAllowList(this.collectionId);3534 }35353536 async getEffectiveLimits() {3537 return await this.helper.collection.getEffectiveLimits(this.collectionId);3538 }35393540 async getProperties(propertyKeys?: string[] | null) {3541 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3542 }35433544 async getPropertiesConsumedSpace() {3545 return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);3546 }35473548 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3549 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3550 }35513552 async getOptions() {3553 return await this.helper.collection.getCollectionOptions(this.collectionId);3554 }35553556 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {3557 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);3558 }35593560 async confirmSponsorship(signer: TSigner) {3561 return await this.helper.collection.confirmSponsorship(signer, this.collectionId);3562 }35633564 async removeSponsor(signer: TSigner) {3565 return await this.helper.collection.removeSponsor(signer, this.collectionId);3566 }35673568 async setLimits(signer: TSigner, limits: ICollectionLimits) {3569 return await this.helper.collection.setLimits(signer, this.collectionId, limits);3570 }35713572 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {3573 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);3574 }35753576 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3577 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);3578 }35793580 async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {3581 return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);3582 }35833584 async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {3585 return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);3586 }35873588 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3589 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);3590 }35913592 async setProperties(signer: TSigner, properties: IProperty[]) {3593 return await this.helper.collection.setProperties(signer, this.collectionId, properties);3594 }35953596 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3597 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);3598 }35993600 async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {3601 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);3602 }36033604 async enableNesting(signer: TSigner, permissions: INestingPermissions) {3605 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);3606 }36073608 async disableNesting(signer: TSigner) {3609 return await this.helper.collection.disableNesting(signer, this.collectionId);3610 }36113612 async burn(signer: TSigner) {3613 return await this.helper.collection.burn(signer, this.collectionId);3614 }36153616 scheduleAt<T extends UniqueHelper>(3617 executionBlockNumber: number,3618 options: ISchedulerOptions = {},3619 ) {3620 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3621 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3622 }36233624 scheduleAfter<T extends UniqueHelper>(3625 blocksBeforeExecution: number,3626 options: ISchedulerOptions = {},3627 ) {3628 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3629 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3630 }36313632 getSudo<T extends UniqueHelper>() {3633 return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());3634 }3635}363636373638export class UniqueNFTCollection extends UniqueBaseCollection {3639 getTokenObject(tokenId: number) {3640 return new UniqueNFToken(tokenId, this);3641 }36423643 async getTokensByAddress(addressObj: ICrossAccountId) {3644 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3645 }36463647 async getToken(tokenId: number, blockHashAt?: string) {3648 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3649 }36503651 async getTokenOwner(tokenId: number, blockHashAt?: string) {3652 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3653 }36543655 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3656 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3657 }36583659 async getTokenChildren(tokenId: number, blockHashAt?: string) {3660 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3661 }36623663 async getPropertyPermissions(propertyKeys: string[] | null = null) {3664 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3665 }36663667 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3668 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3669 }36703671 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3672 const api = this.helper.getApi();3673 const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON();36743675 return (props! as any).consumedSpace;3676 }36773678 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3679 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3680 }36813682 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3683 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3684 }36853686 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3687 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3688 }36893690 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3691 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3692 }36933694 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3695 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3696 }36973698 async mintMultipleTokens(signer: TSigner, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]) {3699 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3700 }37013702 async burnToken(signer: TSigner, tokenId: number) {3703 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3704 }37053706 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3707 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3708 }37093710 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3711 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3712 }37133714 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3715 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3716 }37173718 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3719 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3720 }37213722 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3723 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3724 }37253726 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3727 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3728 }37293730 scheduleAt<T extends UniqueHelper>(3731 executionBlockNumber: number,3732 options: ISchedulerOptions = {},3733 ) {3734 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3735 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3736 }37373738 scheduleAfter<T extends UniqueHelper>(3739 blocksBeforeExecution: number,3740 options: ISchedulerOptions = {},3741 ) {3742 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3743 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3744 }37453746 getSudo<T extends UniqueHelper>() {3747 return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3748 }3749}375037513752export class UniqueRFTCollection extends UniqueBaseCollection {3753 getTokenObject(tokenId: number) {3754 return new UniqueRFToken(tokenId, this);3755 }37563757 async getToken(tokenId: number, blockHashAt?: string) {3758 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3759 }37603761 async getTokenOwner(tokenId: number, blockHashAt?: string) {3762 return await this.helper.rft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3763 }37643765 async getTokensByAddress(addressObj: ICrossAccountId) {3766 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3767 }37683769 async getTop10TokenOwners(tokenId: number) {3770 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3771 }37723773 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3774 return await this.helper.rft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3775 }37763777 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3778 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3779 }37803781 async getTokenTotalPieces(tokenId: number) {3782 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3783 }37843785 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3786 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3787 }37883789 async getPropertyPermissions(propertyKeys: string[] | null = null) {3790 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3791 }37923793 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3794 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3795 }37963797 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3798 const api = this.helper.getApi();3799 const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON();38003801 return (props! as any).consumedSpace;3802 }38033804 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount = 1n) {3805 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3806 }38073808 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {3809 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3810 }38113812 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {3813 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3814 }38153816 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3817 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3818 }38193820 async mintToken(signer: TSigner, pieces = 1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3821 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3822 }38233824 async mintMultipleTokens(signer: TSigner, tokens: { pieces: bigint, owner: ICrossAccountId, properties?: IProperty[] }[]) {3825 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3826 }38273828 async burnToken(signer: TSigner, tokenId: number, amount = 1n) {3829 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3830 }38313832 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n) {3833 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3834 }38353836 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3837 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3838 }38393840 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3841 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3842 }38433844 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3845 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3846 }38473848 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3849 return await this.helper.rft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3850 }38513852 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3853 return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3854 }38553856 scheduleAt<T extends UniqueHelper>(3857 executionBlockNumber: number,3858 options: ISchedulerOptions = {},3859 ) {3860 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3861 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3862 }38633864 scheduleAfter<T extends UniqueHelper>(3865 blocksBeforeExecution: number,3866 options: ISchedulerOptions = {},3867 ) {3868 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3869 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3870 }38713872 getSudo<T extends UniqueHelper>() {3873 return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3874 }3875}387638773878export class UniqueFTCollection extends UniqueBaseCollection {3879 async getBalance(addressObj: ICrossAccountId) {3880 return await this.helper.ft.getBalance(this.collectionId, addressObj);3881 }38823883 async getTotalPieces() {3884 return await this.helper.ft.getTotalPieces(this.collectionId);3885 }38863887 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3888 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3889 }38903891 async getTop10Owners() {3892 return await this.helper.ft.getTop10Owners(this.collectionId);3893 }38943895 async mint(signer: TSigner, amount = 1n, owner: ICrossAccountId = {Substrate: signer.address}) {3896 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3897 }38983899 async mintWithOneOwner(signer: TSigner, tokens: { value: bigint }[], owner: ICrossAccountId = {Substrate: signer.address}) {3900 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3901 }39023903 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {3904 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3905 }39063907 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {3908 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3909 }39103911 async burnTokens(signer: TSigner, amount = 1n) {3912 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3913 }39143915 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {3916 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3917 }39183919 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {3920 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3921 }39223923 scheduleAt<T extends UniqueHelper>(3924 executionBlockNumber: number,3925 options: ISchedulerOptions = {},3926 ) {3927 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3928 return new UniqueFTCollection(this.collectionId, scheduledHelper);3929 }39303931 scheduleAfter<T extends UniqueHelper>(3932 blocksBeforeExecution: number,3933 options: ISchedulerOptions = {},3934 ) {3935 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3936 return new UniqueFTCollection(this.collectionId, scheduledHelper);3937 }39383939 getSudo<T extends UniqueHelper>() {3940 return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3941 }3942}394339443945export class UniqueBaseToken {3946 collection: UniqueNFTCollection | UniqueRFTCollection;3947 collectionId: number;3948 tokenId: number;39493950 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3951 this.collection = collection;3952 this.collectionId = collection.collectionId;3953 this.tokenId = tokenId;3954 }39553956 async getNextSponsored(addressObj: ICrossAccountId) {3957 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3958 }39593960 async getProperties(propertyKeys?: string[] | null) {3961 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3962 }39633964 async getTokenPropertiesConsumedSpace() {3965 return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);3966 }39673968 async setProperties(signer: TSigner, properties: IProperty[]) {3969 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3970 }39713972 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3973 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3974 }39753976 async doesExist() {3977 return await this.collection.doesTokenExist(this.tokenId);3978 }39793980 nestingAccount() {3981 return this.collection.helper.util.getTokenAccount(this);3982 }39833984 scheduleAt<T extends UniqueHelper>(3985 executionBlockNumber: number,3986 options: ISchedulerOptions = {},3987 ) {3988 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3989 return new UniqueBaseToken(this.tokenId, scheduledCollection);3990 }39913992 scheduleAfter<T extends UniqueHelper>(3993 blocksBeforeExecution: number,3994 options: ISchedulerOptions = {},3995 ) {3996 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3997 return new UniqueBaseToken(this.tokenId, scheduledCollection);3998 }39994000 getSudo<T extends UniqueHelper>() {4001 return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());4002 }4003}400440054006export class UniqueNFToken extends UniqueBaseToken {4007 collection: UniqueNFTCollection;40084009 constructor(tokenId: number, collection: UniqueNFTCollection) {4010 super(tokenId, collection);4011 this.collection = collection;4012 }40134014 async getData(blockHashAt?: string) {4015 return await this.collection.getToken(this.tokenId, blockHashAt);4016 }40174018 async getOwner(blockHashAt?: string) {4019 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);4020 }40214022 async getTopmostOwner(blockHashAt?: string) {4023 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);4024 }40254026 async getChildren(blockHashAt?: string) {4027 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);4028 }40294030 async nest(signer: TSigner, toTokenObj: IToken) {4031 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);4032 }40334034 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {4035 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);4036 }40374038 async transfer(signer: TSigner, addressObj: ICrossAccountId) {4039 return await this.collection.transferToken(signer, this.tokenId, addressObj);4040 }40414042 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {4043 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);4044 }40454046 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {4047 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);4048 }40494050 async isApproved(toAddressObj: ICrossAccountId) {4051 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);4052 }40534054 async burn(signer: TSigner) {4055 return await this.collection.burnToken(signer, this.tokenId);4056 }40574058 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {4059 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);4060 }40614062 scheduleAt<T extends UniqueHelper>(4063 executionBlockNumber: number,4064 options: ISchedulerOptions = {},4065 ) {4066 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);4067 return new UniqueNFToken(this.tokenId, scheduledCollection);4068 }40694070 scheduleAfter<T extends UniqueHelper>(4071 blocksBeforeExecution: number,4072 options: ISchedulerOptions = {},4073 ) {4074 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);4075 return new UniqueNFToken(this.tokenId, scheduledCollection);4076 }40774078 getSudo<T extends UniqueHelper>() {4079 return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());4080 }4081}40824083export class UniqueRFToken extends UniqueBaseToken {4084 collection: UniqueRFTCollection;40854086 constructor(tokenId: number, collection: UniqueRFTCollection) {4087 super(tokenId, collection);4088 this.collection = collection;4089 }40904091 async getData(blockHashAt?: string) {4092 return await this.collection.getToken(this.tokenId, blockHashAt);4093 }40944095 async getOwner(blockHashAt?: string) {4096 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);4097 }40984099 async getTop10Owners() {4100 return await this.collection.getTop10TokenOwners(this.tokenId);4101 }41024103 async getTopmostOwner(blockHashAt?: string) {4104 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);4105 }41064107 async nest(signer: TSigner, toTokenObj: IToken) {4108 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);4109 }41104111 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {4112 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);4113 }41144115 async getBalance(addressObj: ICrossAccountId) {4116 return await this.collection.getTokenBalance(this.tokenId, addressObj);4117 }41184119 async getTotalPieces() {4120 return await this.collection.getTokenTotalPieces(this.tokenId);4121 }41224123 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {4124 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);4125 }41264127 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount = 1n) {4128 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);4129 }41304131 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {4132 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);4133 }41344135 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {4136 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);4137 }41384139 async repartition(signer: TSigner, amount: bigint) {4140 return await this.collection.repartitionToken(signer, this.tokenId, amount);4141 }41424143 async burn(signer: TSigner, amount = 1n) {4144 return await this.collection.burnToken(signer, this.tokenId, amount);4145 }41464147 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {4148 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);4149 }41504151 scheduleAt<T extends UniqueHelper>(4152 executionBlockNumber: number,4153 options: ISchedulerOptions = {},4154 ) {4155 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);4156 return new UniqueRFToken(this.tokenId, scheduledCollection);4157 }41584159 scheduleAfter<T extends UniqueHelper>(4160 blocksBeforeExecution: number,4161 options: ISchedulerOptions = {},4162 ) {4163 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);4164 return new UniqueRFToken(this.tokenId, scheduledCollection);4165 }41664167 getSudo<T extends UniqueHelper>() {4168 return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());4169 }4170}