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, blake2AsHex} 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 IPhasicEvent,48} from './types';49import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';50import type {Vec} from '@polkadot/types-codec';51import {FrameSystemEventRecord, PalletDemocracyConviction} from '@polkadot/types/lookup';5253export class CrossAccountId {54 Substrate!: TSubstrateAccount;55 Ethereum!: TEthereumAccount;5657 constructor(account: ICrossAccountId) {58 if('Substrate' in account) this.Substrate = account.Substrate;59 else this.Ethereum = account.Ethereum;60 }6162 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {63 switch (domain) {64 case 'Substrate': return new CrossAccountId({Substrate: account.address});65 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();66 }67 }6869 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {70 if('substrate' in address) return new CrossAccountId({Substrate: address.substrate});71 else return new CrossAccountId({Ethereum: address.ethereum});72 }7374 static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {75 return encodeAddress(decodeAddress(address), ss58Format);76 }7778 static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {79 return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});80 }8182 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {83 if(this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);84 return this;85 }8687 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {88 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));89 }9091 toEthereum(): CrossAccountId {92 if(this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});93 return this;94 }9596 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {97 return evmToAddress(address, ss58Format);98 }99100 toSubstrate(ss58Format?: number): CrossAccountId {101 if(this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});102 return this;103 }104105 toLowerCase(): CrossAccountId {106 if(this.Substrate) this.Substrate = this.Substrate.toLowerCase();107 if(this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();108 return this;109 }110}111112const nesting = {113 toChecksumAddress(address: string): string {114 if(typeof address === 'undefined') return '';115116 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);117118 address = address.toLowerCase().replace(/^0x/i, '');119 const addressHash = keccakAsHex(address).replace(/^0x/i, '');120 const checksumAddress = ['0x'];121122 for(let i = 0; i < address.length; i++) {123 124 if(parseInt(addressHash[i], 16) > 7) {125 checksumAddress.push(address[i].toUpperCase());126 } else {127 checksumAddress.push(address[i]);128 }129 }130 return checksumAddress.join('');131 },132 tokenIdToAddress(collectionId: number, tokenId: number) {133 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);134 },135};136137class UniqueUtil {138 static transactionStatus = {139 NOT_READY: 'NotReady',140 FAIL: 'Fail',141 SUCCESS: 'Success',142 };143144 static chainLogType = {145 EXTRINSIC: 'extrinsic',146 RPC: 'rpc',147 };148149 static getTokenAccount(token: IToken): CrossAccountId {150 return new CrossAccountId({Ethereum: this.getTokenAddress(token)});151 }152153 static getTokenAddress(token: IToken): string {154 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);155 }156157 static getDefaultLogger(): ILogger {158 return {159 log(msg: any, level = 'INFO') {160 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));161 },162 level: {163 ERROR: 'ERROR',164 WARNING: 'WARNING',165 INFO: 'INFO',166 },167 };168 }169170 static vec2str(arr: string[] | number[]) {171 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');172 }173174 static str2vec(string: string) {175 if(typeof string !== 'string') return string;176 return Array.from(string).map(x => x.charCodeAt(0));177 }178179 static fromSeed(seed: string, ss58Format = 42) {180 const keyring = new Keyring({type: 'sr25519', ss58Format});181 return keyring.addFromUri(seed);182 }183184 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {185 if(creationResult.status !== this.transactionStatus.SUCCESS) {186 throw Error('Unable to create collection!');187 }188189 let collectionId = null;190 creationResult.result.events.forEach(({event: {data, method, section}}) => {191 if((section === 'common') && (method === 'CollectionCreated')) {192 collectionId = parseInt(data[0].toString(), 10);193 }194 });195196 if(collectionId === null) {197 throw Error('No CollectionCreated event was found!');198 }199200 return collectionId;201 }202203 static extractTokensFromCreationResult(creationResult: ITransactionResult): {204 success: boolean,205 tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],206 } {207 if(creationResult.status !== this.transactionStatus.SUCCESS) {208 throw Error('Unable to create tokens!');209 }210 let success = false;211 const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];212 creationResult.result.events.forEach(({event: {data, method, section}}) => {213 if(method === 'ExtrinsicSuccess') {214 success = true;215 } else if((section === 'common') && (method === 'ItemCreated')) {216 tokens.push({217 collectionId: parseInt(data[0].toString(), 10),218 tokenId: parseInt(data[1].toString(), 10),219 owner: data[2].toHuman(),220 amount: data[3].toBigInt(),221 });222 }223 });224 return {success, tokens};225 }226227 static extractTokensFromBurnResult(burnResult: ITransactionResult): {228 success: boolean,229 tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],230 } {231 if(burnResult.status !== this.transactionStatus.SUCCESS) {232 throw Error('Unable to burn tokens!');233 }234 let success = false;235 const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];236 burnResult.result.events.forEach(({event: {data, method, section}}) => {237 if(method === 'ExtrinsicSuccess') {238 success = true;239 } else if((section === 'common') && (method === 'ItemDestroyed')) {240 tokens.push({241 collectionId: parseInt(data[0].toString(), 10),242 tokenId: parseInt(data[1].toString(), 10),243 owner: data[2].toHuman(),244 amount: data[3].toBigInt(),245 });246 }247 });248 return {success, tokens};249 }250251 static findCollectionInEvents(events: { event: IEvent }[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {252 let eventId = null;253 events.forEach(({event: {data, method, section}}) => {254 if((section === expectedSection) && (method === expectedMethod)) {255 eventId = parseInt(data[0].toString(), 10);256 }257 });258259 if(eventId === null) {260 throw Error(`No ${expectedMethod} event was found!`);261 }262 return eventId === collectionId;263 }264265 static isTokenTransferSuccess(events: { event: IEvent }[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {266 const normalizeAddress = (address: string | ICrossAccountId) => {267 if(typeof address === 'string') return address;268 const obj = {} as any;269 Object.keys(address).forEach(k => {270 obj[k.toLocaleLowerCase()] = (address as any)[k];271 });272 if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);273 if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();274 return address;275 };276 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;277 events.forEach(({event: {data, method, section}}) => {278 if((section === 'common') && (method === 'Transfer')) {279 const hData = (data as any).toJSON();280 transfer = {281 collectionId: hData[0],282 tokenId: hData[1],283 from: normalizeAddress(hData[2]),284 to: normalizeAddress(hData[3]),285 amount: BigInt(hData[4]),286 };287 }288 });289 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;290 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);291 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);292 isSuccess = isSuccess && amount === transfer.amount;293 return isSuccess;294 }295296 static bigIntToDecimals(number: bigint, decimals = 18) {297 const numberStr = number.toString();298 const dotPos = numberStr.length - decimals;299300 if(dotPos <= 0) {301 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;302 } else {303 const intPart = numberStr.substring(0, dotPos);304 const fractPart = numberStr.substring(dotPos);305 return intPart + '.' + fractPart;306 }307 }308}309310class UniqueEventHelper {311 private static extractIndex(index: any): [number, number] | string {312 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];313 return index.toJSON();314 }315316 private static extractSub(data: any, subTypes: any): { [key: string]: any } {317 let obj: any = {};318 let index = 0;319320 if(data.entries) {321 for(const [key, value] of data.entries()) {322 obj[key] = this.extractData(value, subTypes[index]);323 index++;324 }325 } else obj = data.toJSON();326327 return obj;328 }329330 private static toHuman(data: any) {331 return data && data.toHuman ? data.toHuman() : `${data}`;332 }333334 private static extractData(data: any, type: any): any {335 if(!type) return this.toHuman(data);336 if(['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();337 if(['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();338 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);339 return this.toHuman(data);340 }341342 public static extractEvents(events: { event: any, phase: any }[]): IEvent[] {343 const parsedEvents: IEvent[] = [];344345 events.forEach((record) => {346 const {event, phase} = record;347 const types = event.typeDef;348349 const eventData: IEvent = {350 section: event.section.toString(),351 method: event.method.toString(),352 index: this.extractIndex(event.index),353 data: [],354 phase: phase.toJSON(),355 };356357 event.data.forEach((val: any, index: number) => {358 eventData.data.push(this.extractData(val, types[index]));359 });360361 parsedEvents.push(eventData);362 });363364 return parsedEvents;365 }366}367const InvalidTypeSymbol = Symbol('Invalid type');368369export type Invalid<ErrorMessage> =370 | ((371 invalidType: typeof InvalidTypeSymbol,372 ..._: typeof InvalidTypeSymbol[]373 ) => typeof InvalidTypeSymbol)374 | null375 | undefined;376377type Get2<T, P extends string, E> =378 P extends `${infer Key}.${infer Key2}` ? Key extends keyof T ? Key2 extends keyof T[Key] ? T[Key][Key2] : E : E : E;379type ForceFunction<T> = T extends (...args: any) => any ? T : (...args: any) => Invalid<'not a function'>;380381export class ChainHelperBase {382 helperBase: any;383384 transactionStatus = UniqueUtil.transactionStatus;385 chainLogType = UniqueUtil.chainLogType;386 util: typeof UniqueUtil;387 eventHelper: typeof UniqueEventHelper;388 logger: ILogger;389 api: ApiPromise | null;390 forcedNetwork: TNetworks | null;391 network: TNetworks | null;392 wsEndpoint: string | null;393 chainLog: IUniqueHelperLog[];394 children: ChainHelperBase[];395 address: AddressGroup;396 chain: ChainGroup;397398 constructor(logger?: ILogger, helperBase?: any) {399 this.helperBase = helperBase;400401 this.util = UniqueUtil;402 this.eventHelper = UniqueEventHelper;403 if(typeof logger == 'undefined') logger = this.util.getDefaultLogger();404 this.logger = logger;405 this.api = null;406 this.forcedNetwork = null;407 this.network = null;408 this.wsEndpoint = null;409 this.chainLog = [];410 this.children = [];411 this.address = new AddressGroup(this);412 this.chain = new ChainGroup(this);413 }414415 clone(helperCls: ChainHelperBaseConstructor, options: { [key: string]: any } = {}) {416 Object.setPrototypeOf(helperCls.prototype, this);417 const newHelper = new helperCls(this.logger, options);418419 newHelper.api = this.api;420 newHelper.network = this.network;421 newHelper.forceNetwork = this.forceNetwork;422423 this.children.push(newHelper);424425 return newHelper;426 }427428 getEndpoint(): string {429 if(this.wsEndpoint === null) throw Error('No connection was established');430 return this.wsEndpoint;431 }432433 getApi(): ApiPromise {434 if(this.api === null) throw Error('API not initialized');435 return this.api;436 }437438 async subscribeEvents(expectedEvents: { section: string, names: string[] }[]) {439 const collectedEvents: IEvent[] = [];440 const unsubscribe = await this.getApi().query.system.events((events: Vec<FrameSystemEventRecord>) => {441 const ievents = this.eventHelper.extractEvents(events);442 ievents.forEach((event) => {443 expectedEvents.forEach((e => {444 if(event.section === e.section && e.names.includes(event.method)) {445 collectedEvents.push(event);446 }447 }));448 });449 });450 return {unsubscribe: unsubscribe as any, collectedEvents};451 }452453 clearChainLog(): void {454 this.chainLog = [];455 }456457 forceNetwork(value: TNetworks): void {458 this.forcedNetwork = value;459 }460461 async connect(wsEndpoint: string, listeners?: IApiListeners) {462 if(this.api !== null) throw Error('Already connected');463 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);464 this.wsEndpoint = wsEndpoint;465 this.api = api;466 this.network = network;467 }468469 async disconnect() {470 for(const child of this.children) {471 child.clearApi();472 }473474 if(this.api === null) return;475 await this.api.disconnect();476 this.clearApi();477 }478479 clearApi() {480 this.api = null;481 this.network = null;482 }483484 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {485 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;486 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];487488 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;489490 if(['quartz', 'unique', 'sapphire'].indexOf(spec.specName) > -1) return spec.specName;491 return 'opal';492 }493494 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {495 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});496 await api.isReady;497498 const network = await this.detectNetwork(api);499500 await api.disconnect();501502 return network;503 }504505 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{506 api: ApiPromise;507 network: TNetworks;508 }> {509 if(typeof network === 'undefined' || network === null) network = 'opal';510 const supportedRPC = {511 opal: {512 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,513 },514 quartz: {515 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,516 },517 unique: {518 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,519 },520 rococo: {},521 westend: {},522 moonbeam: {},523 moonriver: {},524 acala: {},525 karura: {},526 westmint: {},527 };528 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);529 const rpc = supportedRPC[network];530531 532 533534 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});535536 await api.isReadyOrError;537538 if(typeof listeners === 'undefined') listeners = {};539 for(const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {540 if(!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;541 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);542 }543544 return {api, network};545 }546547 getTransactionStatus(data: { events: { event: IEvent }[], status: any }) {548 const {events, status} = data;549 if(status.isReady) {550 return this.transactionStatus.NOT_READY;551 }552 if(status.isBroadcast) {553 return this.transactionStatus.NOT_READY;554 }555 if(status.isInBlock || status.isFinalized) {556 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');557 if(errors.length > 0) {558 return this.transactionStatus.FAIL;559 }560 if(events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {561 return this.transactionStatus.SUCCESS;562 }563 }564565 return this.transactionStatus.FAIL;566 }567568 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {569 const sign = (callback: any) => {570 if(options !== null) return transaction.signAndSend(sender, options, callback);571 return transaction.signAndSend(sender, callback);572 };573 574 return new Promise(async (resolve, reject) => {575 try {576 const unsub = await sign((result: any) => {577 const status = this.getTransactionStatus(result);578579 if(status === this.transactionStatus.SUCCESS) {580 this.logger.log(`${label} successful`);581 unsub();582 resolve({result, status, blockHash: result.status.asInBlock.toHuman()});583 } else if(status === this.transactionStatus.FAIL) {584 let moduleError = null;585586 if(result.hasOwnProperty('dispatchError')) {587 const dispatchError = result['dispatchError'];588589 if(dispatchError) {590 if(dispatchError.isModule) {591 const modErr = dispatchError.asModule;592 const errorMeta = dispatchError.registry.findMetaError(modErr);593594 moduleError = `${errorMeta.section}.${errorMeta.name}`;595 } else if(dispatchError.isToken) {596 moduleError = `Token: ${dispatchError.asToken}`;597 } else {598 599 moduleError = `Misc: ${dispatchError.toHuman()}`;600 }601 } else {602 this.logger.log(result, this.logger.level.ERROR);603 }604 }605606 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);607 unsub();608 reject({status, moduleError, result});609 }610 });611 } catch (e) {612 this.logger.log(e, this.logger.level.ERROR);613 reject(e);614 }615 });616 }617618 async signTransactionWithoutSending(signer: TSigner, tx: any) {619 const api = this.getApi();620 const signingInfo = await api.derive.tx.signingInfo(signer.address);621622 tx.sign(signer, {623 blockHash: api.genesisHash,624 genesisHash: api.genesisHash,625 runtimeVersion: api.runtimeVersion,626 nonce: signingInfo.nonce,627 });628629 return tx.toHex();630 }631632 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {633 const api = this.getApi();634 const signingInfo = await api.derive.tx.signingInfo(signer.address);635636 637 638 tx.sign(signer, {639 blockHash: api.genesisHash,640 genesisHash: api.genesisHash,641 runtimeVersion: api.runtimeVersion,642 nonce: signingInfo.nonce,643 });644645 if(len === null) {646 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;647 } else {648 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;649 }650 }651652 constructApiCall(apiCall: string, params: any[]) {653 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);654 let call = this.getApi() as any;655 for(const part of apiCall.slice(4).split('.')) {656 call = call[part];657 if(!call) {658 const advice = part.includes('_') ? ' Looks like it needs to be converted to camel case.' : '';659 throw Error(`Function ${part} of api call ${apiCall} not found.${advice}`);660 }661 }662 return call(...params);663 }664665 encodeApiCall(apiCall: string, params: any[]) {666 return this.constructApiCall(apiCall, params).method.toHex();667 }668669 async executeExtrinsic<670 E extends string,671 V extends (672 ...args: any) => any = ForceFunction<673 Get2<674 AugmentedSubmittables<'promise'>,675 E, (...args: any) => Invalid<'not found'>676 >677 >678 >(679 sender: TSigner,680 extrinsic: `api.tx.${E}`,681 params: Parameters<V>,682 expectSuccess = true,683 options: Partial<SignerOptions> | null = null,684 ): Promise<ITransactionResult> {685 if(this.api === null) throw Error('API not initialized');686687 const startTime = (new Date()).getTime();688 let result: ITransactionResult;689 let events: IEvent[] = [];690 try {691 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;692 events = this.eventHelper.extractEvents(result.result.events);693 const errorEvent = events.find((event) => event.method == 'ExecutedFailed' || event.method == 'CreatedFailed');694 if(errorEvent)695 throw Error(errorEvent.method + ': ' + extrinsic);696 }697 catch (e) {698 if(!(e as object).hasOwnProperty('status')) throw e;699 result = e as ITransactionResult;700 }701702 const endTime = (new Date()).getTime();703704 const log = {705 executedAt: endTime,706 executionTime: endTime - startTime,707 type: this.chainLogType.EXTRINSIC,708 status: result.status,709 call: extrinsic,710 signer: this.getSignerAddress(sender),711 params,712 } as IUniqueHelperLog;713714 let errorMessage = '';715716 if(result.status !== this.transactionStatus.SUCCESS) {717 if(result.moduleError) {718 errorMessage = typeof result.moduleError === 'string'719 ? result.moduleError720 : `${Object.keys(result.moduleError)[0]}: ${Object.values(result.moduleError)[0]}`;721 log.moduleError = errorMessage;722 }723 else if(result.result.dispatchError) log.dispatchError = result.result.dispatchError;724 }725 if(events.length > 0) log.events = events;726727 this.chainLog.push(log);728729 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {730 if(result.moduleError) throw Error(`${errorMessage}`);731 else if(result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));732 }733 return result as any;734 }735736 async callRpc737 738 739 740 741 742 743 744 745 746 747 748 749 (rpc: string, params?: any[]): Promise<any> {750751 if(typeof params === 'undefined') params = [] as any;752 if(this.api === null) throw Error('API not initialized');753 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);754755 const startTime = (new Date()).getTime();756 let result;757 let error = null;758 const log = {759 type: this.chainLogType.RPC,760 call: rpc,761 params,762 } as any as IUniqueHelperLog;763764 try {765 result = await this.constructApiCall(rpc, params as any);766 }767 catch (e) {768 error = e;769 }770771 const endTime = (new Date()).getTime();772773 log.executedAt = endTime;774 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';775 log.executionTime = endTime - startTime;776777 this.chainLog.push(log);778779 if(error !== null) throw error;780781 return result;782 }783784 getSignerAddress(signer: IKeyringPair | string): string {785 if(typeof signer === 'string') return signer;786 return signer.address;787 }788789 fetchAllPalletNames(): string[] {790 if(this.api === null) throw Error('API not initialized');791 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()).sort();792 }793794 fetchMissingPalletNames(requiredPallets: readonly string[]): string[] {795 const palletNames = this.fetchAllPalletNames();796 return requiredPallets.filter(p => !palletNames.includes(p));797 }798}799800801class HelperGroup<T extends ChainHelperBase> {802 helper: T;803804 constructor(uniqueHelper: T) {805 this.helper = uniqueHelper;806 }807}808809810class CollectionGroup extends HelperGroup<UniqueHelper> {811 812813814815816817818819820 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {821 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();822 }823824 825826827828829 async getTotalCount(): Promise<number> {830 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();831 }832833 834835836837838839840841842 async getData(collectionId: number): Promise<{843 id: number;844 name: string;845 description: string;846 tokensCount: number;847 admins: CrossAccountId[];848 normalizedOwner: TSubstrateAccount;849 raw: any850 } | null> {851 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);852 const humanCollection = collection.toHuman(), collectionData = {853 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],854 raw: humanCollection,855 } as any, jsonCollection = collection.toJSON();856 if(humanCollection === null) return null;857 collectionData.raw.limits = jsonCollection.limits;858 collectionData.raw.permissions = jsonCollection.permissions;859 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);860 for(const key of ['name', 'description']) {861 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);862 }863864 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))865 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)866 : 0;867 collectionData.admins = await this.getAdmins(collectionId);868869 return collectionData;870 }871872 873874875876877878879880 async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {881 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();882883 return normalize884 ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())885 : admins;886 }887888 889890891892893894895 async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {896 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();897 return normalize898 ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())899 : allowListed;900 }901902 903904905906907908909 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {910 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();911 }912913 914915916917918919920921 async burn(signer: TSigner, collectionId: number): Promise<boolean> {922 const result = await this.helper.executeExtrinsic(923 signer,924 'api.tx.unique.destroyCollection', [collectionId],925 true,926 );927928 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');929 }930931 932933934935936937938939940 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {941 const result = await this.helper.executeExtrinsic(942 signer,943 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],944 true,945 );946947 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');948 }949950 951952953954955956957958 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {959 const result = await this.helper.executeExtrinsic(960 signer,961 'api.tx.unique.confirmSponsorship', [collectionId],962 true,963 );964965 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');966 }967968 969970971972973974975976 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {977 const result = await this.helper.executeExtrinsic(978 signer,979 'api.tx.unique.removeCollectionSponsor', [collectionId],980 true,981 );982983 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');984 }985986 9879889899909919929939949959969979989991000100110021003 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {1004 const result = await this.helper.executeExtrinsic(1005 signer,1006 'api.tx.unique.setCollectionLimits', [collectionId, limits],1007 true,1008 );10091010 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');1011 }10121013 101410151016101710181019102010211022 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {1023 const result = await this.helper.executeExtrinsic(1024 signer,1025 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],1026 true,1027 );10281029 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');1030 }10311032 103310341035103610371038103910401041 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {1042 const result = await this.helper.executeExtrinsic(1043 signer,1044 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],1045 true,1046 );10471048 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');1049 }10501051 105210531054105510561057105810591060 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {1061 const result = await this.helper.executeExtrinsic(1062 signer,1063 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],1064 true,1065 );10661067 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');1068 }10691070 10711072107310741075107610771078 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {1079 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();1080 }10811082 1083108410851086108710881089 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1090 const result = await this.helper.executeExtrinsic(1091 signer,1092 'api.tx.unique.addToAllowList', [collectionId, addressObj],1093 true,1094 );10951096 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');1097 }10981099 11001101110211031104110511061107 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1108 const result = await this.helper.executeExtrinsic(1109 signer,1110 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1111 true,1112 );11131114 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');1115 }11161117 111811191120112111221123112411251126 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1127 const result = await this.helper.executeExtrinsic(1128 signer,1129 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1130 true,1131 );11321133 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');1134 }11351136 113711381139114011411142114311441145 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1146 return await this.setPermissions(signer, collectionId, {nesting: permissions});1147 }11481149 11501151115211531154115511561157 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1158 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1159 }11601161 116211631164116511661167116811691170 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1171 const result = await this.helper.executeExtrinsic(1172 signer,1173 'api.tx.unique.setCollectionProperties', [collectionId, properties],1174 true,1175 );11761177 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1178 }11791180 11811182118311841185118611871188 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1189 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1190 }11911192 async getPropertiesConsumedSpace(collectionId: number): Promise<number> {1193 const api = this.helper.getApi();1194 const props = (await api.query.common.collectionProperties(collectionId)).toJSON();11951196 return (props! as any).consumedSpace;1197 }11981199 async getCollectionOptions(collectionId: number) {1200 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1201 }12021203 120412051206120712081209121012111212 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1213 const result = await this.helper.executeExtrinsic(1214 signer,1215 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1216 true,1217 );12181219 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1220 }12211222 12231224122512261227122812291230123112321233 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1234 const result = await this.helper.executeExtrinsic(1235 signer,1236 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1237 true, 1238 );12391240 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1241 }12421243 1244124512461247124812491250125112521253125412551256 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1257 const result = await this.helper.executeExtrinsic(1258 signer,1259 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1260 true, 1261 );1262 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1263 }12641265 12661267126812691270127112721273127412751276 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {1277 const burnResult = await this.helper.executeExtrinsic(1278 signer,1279 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1280 true, 1281 );1282 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1283 if(burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1284 return burnedTokens.success;1285 }12861287 12881289129012911292129312941295129612971298 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1299 const burnResult = await this.helper.executeExtrinsic(1300 signer,1301 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1302 true, 1303 );1304 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1305 return burnedTokens.success && burnedTokens.tokens.length > 0;1306 }13071308 1309131013111312131313141315131613171318 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1319 const approveResult = await this.helper.executeExtrinsic(1320 signer,1321 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1322 true, 1323 );13241325 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1326 }13271328 13291330133113321333133413351336133713381339 async approveTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {1340 const approveResult = await this.helper.executeExtrinsic(1341 signer,1342 'api.tx.unique.approveFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1343 true, 1344 );13451346 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1347 }13481349 1350135113521353135413551356135713581359 async approveTokenFromEth(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1360 const ethMirror = CrossAccountId.fromKeyring(signer).toEthereum();1361 return await this.approveTokenFrom(signer, collectionId, tokenId, ethMirror, toAddressObj, amount);1362 }13631364 1365136613671368136913701371137213731374 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1375 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1376 }13771378 1379138013811382138313841385 async getLastTokenId(collectionId: number): Promise<number> {1386 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1387 }13881389 13901391139213931394139513961397 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1398 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1399 }1400}14011402class NFTnRFT extends CollectionGroup {1403 14041405140614071408140914101411 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1412 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1413 }14141415 1416141714181419142014211422142314241425 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1426 properties: IProperty[];1427 owner: CrossAccountId;1428 normalizedOwner: CrossAccountId;1429 } | null> {1430 let tokenData;1431 if(typeof blockHashAt === 'undefined') {1432 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1433 }1434 else {1435 if(propertyKeys.length == 0) {1436 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1437 if(!collection) return null;1438 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1439 }1440 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1441 }1442 tokenData = tokenData.toHuman();1443 if(tokenData === null || tokenData.owner === null) return null;1444 const owner = {} as any;1445 for(const key of Object.keys(tokenData.owner)) {1446 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1447 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1448 : tokenData.owner[key];1449 }1450 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1451 return tokenData;1452 }14531454 14551456145714581459146014611462 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1463 let owner;1464 if(typeof blockHashAt === 'undefined') {1465 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1466 } else {1467 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1468 }1469 return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1470 }14711472 14731474147514761477147814791480 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1481 let owner;1482 if(typeof blockHashAt === 'undefined') {1483 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1484 } else {1485 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1486 }14871488 if(owner === null) return null;14891490 return owner.toHuman();1491 }14921493 14941495149614971498149915001501 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1502 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1503 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1504 if(!result) {1505 throw Error('Unable to nest token!');1506 }1507 return result;1508 }15091510 151115121513151415151516151715181519 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1520 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1521 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1522 if(!result) {1523 throw Error('Unable to unnest token!');1524 }1525 return result;1526 }15271528 15291530153115321533153415351536153715381539 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1540 const result = await this.helper.executeExtrinsic(1541 signer,1542 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1543 true,1544 );15451546 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1547 }15481549 15501551155215531554155515561557 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1558 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1559 }15601561 1562156315641565156615671568156915701571 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1572 const result = await this.helper.executeExtrinsic(1573 signer,1574 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1575 true,1576 );15771578 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1579 }15801581 158215831584158515861587158815891590 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1591 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1592 }15931594 159515961597159815991600160116021603 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1604 const result = await this.helper.executeExtrinsic(1605 signer,1606 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1607 true,1608 );16091610 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1611 }16121613 161416151616161716181619162016211622 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1623 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1624 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1625 for(const key of ['name', 'description', 'tokenPrefix']) {1626 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);1627 }16281629 let flags = 0;1630 1631 if(collectionOptions.flags) {1632 for(let i = 0; i < collectionOptions.flags.length; i++){1633 const flag = collectionOptions.flags[i];1634 flags = flags | flag;1635 }1636 }1637 collectionOptions.flags = [flags];16381639 const creationResult = await this.helper.executeExtrinsic(1640 signer,1641 'api.tx.unique.createCollectionEx', [collectionOptions],1642 true, 1643 );1644 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1645 }16461647 getCollectionObject(_collectionId: number): any {1648 return null;1649 }16501651 getTokenObject(_collectionId: number, _tokenId: number): any {1652 return null;1653 }16541655 1656165716581659166016611662 async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1663 return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1664 }16651666 166716681669167016711672 async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1673 const result = await this.helper.executeExtrinsic(1674 signer,1675 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1676 true,1677 );1678 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1679 }1680}168116821683class NFTGroup extends NFTnRFT {1684 168516861687168816891690 getCollectionObject(collectionId: number): UniqueNFTCollection {1691 return new UniqueNFTCollection(collectionId, this.helper);1692 }16931694 1695169616971698169917001701 getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1702 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1703 }17041705 1706170717081709171017111712 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1713 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1714 }17151716 1717171817191720172117221723172417251726 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1727 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1728 }17291730 173117321733173417351736173717381739174017411742 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1743 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1744 }17451746 17471748174917501751175217531754 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1755 let children;1756 if(typeof blockHashAt === 'undefined') {1757 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1758 } else {1759 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1760 }17611762 return children.toJSON().map((x: any) => ({collectionId: x.collection, tokenId: x.token}));1763 }17641765 176617671768176917701771177217731774177517761777 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1778 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1779 }17801781 178217831784178517861787 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1788 const creationResult = await this.helper.executeExtrinsic(1789 signer,1790 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1791 NFT: {1792 properties: data.properties,1793 },1794 }],1795 true,1796 );1797 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1798 if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1799 if(createdTokens.tokens.length < 1) throw Error('No tokens minted');1800 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1801 }18021803 180418051806180718081809181018111812181318141815181618171818 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {1819 const creationResult = await this.helper.executeExtrinsic(1820 signer,1821 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1822 true,1823 );1824 const collection = this.getCollectionObject(collectionId);1825 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1826 }18271828 182918301831183218331834183518361837183818391840184118421843184418451846 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {1847 const rawTokens = [];1848 for(const token of tokens) {1849 const raw = {NFT: {properties: token.properties}};1850 rawTokens.push(raw);1851 }1852 const creationResult = await this.helper.executeExtrinsic(1853 signer,1854 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1855 true,1856 );1857 const collection = this.getCollectionObject(collectionId);1858 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1859 }18601861 1862186318641865186618671868186918701871 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1872 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1873 }1874}187518761877class RFTGroup extends NFTnRFT {1878 187918801881188218831884 getCollectionObject(collectionId: number): UniqueRFTCollection {1885 return new UniqueRFTCollection(collectionId, this.helper);1886 }18871888 1889189018911892189318941895 getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1896 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1897 }18981899 1900190119021903190419051906 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1907 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1908 }19091910 19111912191319141915191619171918 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1919 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1920 }19211922 1923192419251926192719281929193019311932 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1933 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1934 }19351936 19371938193919401941194219431944194519461947 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1948 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1949 }19501951 195219531954195519561957195819591960196119621963 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1964 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1965 }19661967 1968196919701971197219731974 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1975 const creationResult = await this.helper.executeExtrinsic(1976 signer,1977 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1978 ReFungible: {1979 pieces: data.pieces,1980 properties: data.properties,1981 },1982 }],1983 true,1984 );1985 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1986 if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1987 if(createdTokens.tokens.length < 1) throw Error('No tokens minted');1988 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1989 }19901991 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {1992 throw Error('Not implemented');1993 const creationResult = await this.helper.executeExtrinsic(1994 signer,1995 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1996 true, 1997 );1998 const collection = this.getCollectionObject(collectionId);1999 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));2000 }20012002 200320042005200620072008200920102011 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {2012 const rawTokens = [];2013 for(const token of tokens) {2014 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};2015 rawTokens.push(raw);2016 }2017 const creationResult = await this.helper.executeExtrinsic(2018 signer,2019 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2020 true,2021 );2022 const collection = this.getCollectionObject(collectionId);2023 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));2024 }20252026 202720282029203020312032203320342035 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {2036 return await super.burnToken(signer, collectionId, tokenId, amount);2037 }20382039 2040204120422043204420452046204720482049 async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {2050 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);2051 }20522053 20542055205620572058205920602061206220632064 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {2065 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);2066 }20672068 2069207020712072207320742075 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {2076 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();2077 }20782079 208020812082208320842085208620872088 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {2089 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);2090 const repartitionResult = await this.helper.executeExtrinsic(2091 signer,2092 'api.tx.unique.repartition', [collectionId, tokenId, amount],2093 true,2094 );2095 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');2096 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');2097 }2098}209921002101class FTGroup extends CollectionGroup {2102 210321042105210621072108 getCollectionObject(collectionId: number): UniqueFTCollection {2109 return new UniqueFTCollection(collectionId, this.helper);2110 }21112112 2113211421152116211721182119212021212122212321242125 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {2126 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 2127 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');2128 collectionOptions.mode = {fungible: decimalPoints};2129 for(const key of ['name', 'description', 'tokenPrefix']) {2130 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);2131 }2132 const creationResult = await this.helper.executeExtrinsic(2133 signer,2134 'api.tx.unique.createCollectionEx', [collectionOptions],2135 true,2136 );2137 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));2138 }21392140 214121422143214421452146214721482149 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {2150 const creationResult = await this.helper.executeExtrinsic(2151 signer,2152 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {2153 Fungible: {2154 value: amount,2155 },2156 }],2157 true, 2158 );2159 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2160 }21612162 21632164216521662167216821692170 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: { value: bigint }[], owner: ICrossAccountId): Promise<boolean> {2171 const rawTokens = [];2172 for(const token of tokens) {2173 const raw = {Fungible: {Value: token.value}};2174 rawTokens.push(raw);2175 }2176 const creationResult = await this.helper.executeExtrinsic(2177 signer,2178 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2179 true,2180 );2181 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2182 }21832184 218521862187218821892190 async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2191 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2192 }21932194 2195219621972198219922002201 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2202 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2203 }22042205 220622072208220922102211221222132214 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {2215 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2216 }22172218 2219222022212222222322242225222622272228 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {2229 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2230 }22312232 22332234223522362237223822392240 async burnTokens(signer: IKeyringPair, collectionId: number, amount = 1n): Promise<boolean> {2241 return await super.burnToken(signer, collectionId, 0, amount);2242 }22432244 224522462247224822492250225122522253 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {2254 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2255 }22562257 22582259226022612262 async getTotalPieces(collectionId: number): Promise<bigint> {2263 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2264 }22652266 2267226822692270227122722273227422752276 approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {2277 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2278 }22792280 2281228222832284228522862287 getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2288 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2289 }2290}229122922293class ChainGroup extends HelperGroup<ChainHelperBase> {2294 22952296229722982299 getChainProperties(): IChainProperties {2300 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2301 return {2302 ss58Format: properties.ss58Format.toJSON(),2303 tokenDecimals: properties.tokenDecimals.toJSON(),2304 tokenSymbol: properties.tokenSymbol.toJSON(),2305 };2306 }23072308 23092310231123122313 async getLatestBlockNumber(): Promise<number> {2314 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2315 }23162317 231823192320232123222323 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2324 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2325 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2326 return blockHash;2327 }23282329 2330 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2331 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2332 if(!blockHash) return null;2333 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2334 }23352336 2337233823392340 async getRelayBlockNumber(): Promise<bigint> {2341 const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber;2342 return BigInt(blockNumber);2343 }23442345 234623472348234923502351 async getNonce(address: TSubstrateAccount): Promise<number> {2352 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2353 }2354}23552356class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2357 235823592360236123622363 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2364 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2365 }23662367 23682369237023712372237323742375 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2376 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);23772378 let transfer = {from: null, to: null, amount: 0n} as any;2379 result.result.events.forEach(({event: {data, method, section}}) => {2380 if((section === 'balances') && (method === 'Transfer')) {2381 transfer = {2382 from: this.helper.address.normalizeSubstrate(data[0]),2383 to: this.helper.address.normalizeSubstrate(data[1]),2384 amount: BigInt(data[2]),2385 };2386 }2387 });2388 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2389 && this.helper.address.normalizeSubstrate(address) === transfer.to2390 && BigInt(amount) === transfer.amount;2391 return isSuccess;2392 }23932394 23952396239723982399 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2400 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2401 return {free: accountInfo.free.toBigInt(), frozen: accountInfo.frozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2402 }24032404 2405240624072408 async getTotalIssuance(): Promise<bigint> {2409 const total = (await this.helper.callRpc('api.query.balances.totalIssuance', []));2410 return total.toBigInt();2411 }24122413 async getLocked(address: TSubstrateAccount): Promise<{ id: string, amount: bigint, reason: string }[]> {2414 const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman();2415 return locks.map((lock: any) => ({id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons}));2416 }2417 async getFrozen(address: TSubstrateAccount): Promise<{ id: string, amount: bigint }[]> {2418 const locks = (await this.helper.api!.query.balances.freezes(address)) as unknown as Array<any>;2419 return locks.map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()}));2420 }2421}24222423class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2424 242524262427242824292430 async getEthereum(address: TEthereumAccount): Promise<bigint> {2431 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2432 }24332434 24352436243724382439244024412442 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2443 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);24442445 let transfer = {from: null, to: null, amount: 0n} as any;2446 result.result.events.forEach(({event: {data, method, section}}) => {2447 if((section === 'balances') && (method === 'Transfer')) {2448 transfer = {2449 from: data[0].toString(),2450 to: data[1].toString(),2451 amount: BigInt(data[2]),2452 };2453 }2454 });2455 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2456 && address === transfer.to2457 && BigInt(amount) === transfer.amount;2458 return isSuccess;2459 }2460}24612462class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2463 subBalanceGroup: SubstrateBalanceGroup<T>;2464 ethBalanceGroup: EthereumBalanceGroup<T>;24652466 constructor(helper: T) {2467 super(helper);2468 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2469 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2470 }24712472 getCollectionCreationPrice(): bigint {2473 return 2n * this.getOneTokenNominal();2474 }2475 24762477247824792480 getOneTokenNominal(): bigint {2481 const chainProperties = this.helper.chain.getChainProperties();2482 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2483 }24842485 248624872488248924902491 getSubstrate(address: TSubstrateAccount): Promise<bigint> {2492 return this.subBalanceGroup.getSubstrate(address);2493 }24942495 24962497249824992500 getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2501 return this.subBalanceGroup.getSubstrateFull(address);2502 }25032504 2505250625072508 getTotalIssuance(): Promise<bigint> {2509 return this.subBalanceGroup.getTotalIssuance();2510 }25112512 251325142515251625172518 getLocked(address: TSubstrateAccount) {2519 return this.subBalanceGroup.getLocked(address);2520 }25212522 25232524252525262527 getFrozen(address: TSubstrateAccount) {2528 return this.subBalanceGroup.getFrozen(address);2529 }25302531 253225332534253525362537 getEthereum(address: TEthereumAccount): Promise<bigint> {2538 return this.ethBalanceGroup.getEthereum(address);2539 }25402541 async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint) {2542 await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceSetBalance', [address, amount], true);2543 }25442545 25462547254825492550255125522553 transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2554 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2555 }25562557 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2558 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);25592560 let transfer = {from: null, to: null, amount: 0n} as any;2561 result.result.events.forEach(({event: {data, method, section}}) => {2562 if((section === 'balances') && (method === 'Transfer')) {2563 transfer = {2564 from: this.helper.address.normalizeSubstrate(data[0]),2565 to: this.helper.address.normalizeSubstrate(data[1]),2566 amount: BigInt(data[2]),2567 };2568 }2569 });2570 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2571 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2572 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2573 return isSuccess;2574 }25752576 2577257825792580258125822583 async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: { start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }): Promise<void> {2584 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, schedule]);2585 const event = result.result.events2586 .find(e => e.event.section === 'vesting' &&2587 e.event.method === 'VestingScheduleAdded' &&2588 e.event.data[0].toHuman() === signer.address);2589 if(!event) throw Error('Cannot find transfer in events');2590 }25912592 25932594259525962597 async getVestingSchedules(address: TSubstrateAccount): Promise<{ start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }[]> {2598 const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON();2599 return schedule.map((schedule: any) => ({2600 start: BigInt(schedule.start),2601 period: BigInt(schedule.period),2602 periodCount: BigInt(schedule.periodCount),2603 perPeriod: BigInt(schedule.perPeriod),2604 }));2605 }26062607 2608260926102611 async claim(signer: TSigner) {2612 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []);2613 const event = result.result.events2614 .find(e => e.event.section === 'vesting' &&2615 e.event.method === 'Claimed' &&2616 e.event.data[0].toHuman() === signer.address);2617 if(!event) throw Error('Cannot find claim in events');2618 }2619}26202621class AddressGroup extends HelperGroup<ChainHelperBase> {2622 2623262426252626262726282629 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2630 return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2631 }26322633 263426352636263726382639 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2640 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2641 }26422643 2644264526462647264826492650 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat = false): TSubstrateAccount {2651 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2652 }26532654 265526562657265826592660 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2661 return CrossAccountId.translateSubToEth(subAddress);2662 }26632664 266526662667266826692670 encodeSubstrateAddress(key: Uint8Array | string | bigint, ss58Format = 42): string {2671 const u8a: Uint8Array = typeof key === 'string'2672 ? hexToU8a(key)2673 : typeof key === 'bigint'2674 ? hexToU8a(key.toString(16))2675 : key;26762677 if(ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2678 throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2679 }26802681 const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2682 if(!allowedDecodedLengths.includes(u8a.length)) {2683 throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2684 }26852686 const u8aPrefix = ss58Format < 642687 ? new Uint8Array([ss58Format])2688 : new Uint8Array([2689 ((ss58Format & 0xfc) >> 2) | 0x40,2690 (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2691 ]);26922693 const input = u8aConcat(u8aPrefix, u8a);26942695 return base58Encode(u8aConcat(2696 input,2697 blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2698 ));2699 }27002701 27022703270427052706 restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2707 if(this.helper.api === null) {2708 throw 'Not connected';2709 }2710 const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2711 if(res === undefined || res === null) {2712 throw 'Restore address error';2713 }2714 return res.toString();2715 }27162717 27182719272027212722 convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2723 if(ethCrossAccount.sub === '0') {2724 return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2725 }27262727 const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2728 return {Substrate: ss58};2729 }27302731 paraSiblingSovereignAccount(paraid: number) {2732 2733 2734 const siblingPrefix = '0x7369626c';27352736 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2737 const suffix = '000000000000000000000000000000000000000000000000';27382739 return siblingPrefix + encodedParaId + suffix;2740 }2741}27422743class StakingGroup extends HelperGroup<UniqueHelper> {2744 2745274627472748274927502751 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2752 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2753 const _stakeResult = await this.helper.executeExtrinsic(2754 signer, 'api.tx.appPromotion.stake',2755 [amountToStake], true,2756 );2757 2758 return true;2759 }27602761 2762276327642765276627672768 async unstakeAll(signer: TSigner, label?: string): Promise<string> {2769 if(typeof label === 'undefined') label = `${signer.address}`;2770 const unstakeResult = await this.helper.executeExtrinsic(2771 signer, 'api.tx.appPromotion.unstakeAll',2772 [], true,2773 );2774 return unstakeResult.blockHash;2775 }27762777 2778277927802781278227832784 async unstakePartial(signer: TSigner, amount: bigint, label?: string): Promise<string> {2785 if(typeof label === 'undefined') label = `${signer.address}`;2786 const unstakeResult = await this.helper.executeExtrinsic(2787 signer, 'api.tx.appPromotion.unstakePartial',2788 [amount], true,2789 );2790 return unstakeResult.blockHash;2791 }27922793 27942795279627972798 async getStakesNumber(address: ICrossAccountId): Promise<number> {2799 if('Ethereum' in address) throw Error('only substrate address');2800 return (await this.helper.callRpc('api.query.appPromotion.stakesPerAccount', [address.Substrate])).toNumber();2801 }28022803 28042805280628072808 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2809 if(address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2810 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2811 }28122813 28142815281628172818 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2819 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2820 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => ({2821 block: block.toBigInt(),2822 amount: amount.toBigInt(),2823 }));2824 }28252826 28272828282928302831 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2832 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2833 }28342835 28362837283828392840 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2841 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2842 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => ({2843 block: block.toBigInt(),2844 amount: amount.toBigInt(),2845 }));2846 return result;2847 }2848}28492850class SchedulerGroup extends HelperGroup<UniqueHelper> {2851 constructor(helper: UniqueHelper) {2852 super(helper);2853 }28542855 cancelScheduled(signer: TSigner, scheduledId: string) {2856 return this.helper.executeExtrinsic(2857 signer,2858 'api.tx.scheduler.cancelNamed',2859 [scheduledId],2860 true,2861 );2862 }28632864 changePriority(signer: TSigner, scheduledId: string, priority: number) {2865 return this.helper.executeExtrinsic(2866 signer,2867 'api.tx.scheduler.changeNamedPriority',2868 [scheduledId, priority],2869 true,2870 );2871 }28722873 scheduleAt<T extends UniqueHelper>(2874 executionBlockNumber: number,2875 options: ISchedulerOptions = {},2876 ) {2877 return this.schedule<T>('schedule', executionBlockNumber, options);2878 }28792880 scheduleAfter<T extends UniqueHelper>(2881 blocksBeforeExecution: number,2882 options: ISchedulerOptions = {},2883 ) {2884 return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);2885 }28862887 schedule<T extends UniqueHelper>(2888 scheduleFn: 'schedule' | 'scheduleAfter',2889 blocksNum: number,2890 options: ISchedulerOptions = {},2891 ) {2892 2893 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2894 return this.helper.clone(ScheduledHelperType, {2895 scheduleFn,2896 blocksNum,2897 options,2898 }) as T;2899 }2900}29012902class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2903 2904 addInvulnerable(signer: TSigner, address: string) {2905 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);2906 }29072908 removeInvulnerable(signer: TSigner, address: string) {2909 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);2910 }29112912 async getInvulnerables(): Promise<string[]> {2913 return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());2914 }29152916 2917 maxCollators(): number {2918 return (this.helper.getApi().consts.configuration.defaultCollatorSelectionMaxCollators.toJSON() as number);2919 }29202921 async getDesiredCollators(): Promise<number> {2922 return (await this.helper.callRpc('api.query.configuration.collatorSelectionDesiredCollatorsOverride')).toNumber();2923 }29242925 setLicenseBond(signer: TSigner, amount: bigint) {2926 return this.helper.executeExtrinsic(signer, 'api.tx.configuration.setCollatorSelectionLicenseBond', [amount]);2927 }29282929 async getLicenseBond(): Promise<bigint> {2930 return (await this.helper.callRpc('api.query.configuration.collatorSelectionLicenseBondOverride')).toBigInt();2931 }29322933 obtainLicense(signer: TSigner) {2934 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.getLicense', []);2935 }29362937 releaseLicense(signer: TSigner) {2938 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);2939 }29402941 forceReleaseLicense(signer: TSigner, released: string) {2942 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);2943 }29442945 async hasLicense(address: string): Promise<bigint> {2946 return (await this.helper.callRpc('api.query.collatorSelection.licenseDepositOf', [address])).toBigInt();2947 }29482949 onboard(signer: TSigner) {2950 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.onboard', []);2951 }29522953 offboard(signer: TSigner) {2954 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.offboard', []);2955 }29562957 async getCandidates(): Promise<string[]> {2958 return (await this.helper.callRpc('api.query.collatorSelection.candidates')).map((x: any) => x.toHuman());2959 }2960}29612962class CollectiveGroup extends HelperGroup<UniqueHelper> {2963 296429652966 private collective: string;29672968 constructor(helper: UniqueHelper, collective: string) {2969 super(helper);2970 this.collective = collective;2971 }29722973 29742975297629772978 private checkExecutedEvent(events: IPhasicEvent[]): string {2979 const executionEvents = events.filter(x =>2980 x.event.section === this.collective && (x.event.method === 'Executed' || x.event.method === 'MemberExecuted'));29812982 if(executionEvents.length != 1) {2983 if(events.filter(x => x.event.section === this.collective && x.event.method === 'Disapproved').length > 0)2984 throw new Error(`Disapproved by ${this.collective}`);2985 else2986 throw new Error(`Expected one 'Executed' or 'MemberExecuted' event for ${this.collective}`);2987 }29882989 const result = (executionEvents[0].event.data as any).result;29902991 if(result.isErr) {2992 if(result.asErr.isModule) {2993 const error = result.asErr.asModule;2994 const metaError = this.helper.getApi()?.registry.findMetaError(error);2995 throw new Error(`Proposal execution failed with ${metaError.section}.${metaError.name}`);2996 } else {2997 throw new Error('Proposal execution failed with ' + result.asErr.toHuman());2998 }2999 }30003001 return (executionEvents[0].event.data as any).proposalHash;3002 }30033004 300530063007 async getMembers() {3008 return (await this.helper.callRpc(`api.query.${this.collective}.members`, [])).toHuman();3009 }30103011 301230133014 async getPrimeMember() {3015 return (await this.helper.callRpc(`api.query.${this.collective}.prime`, [])).toHuman();3016 }30173018 301930203021 async getProposals() {3022 return (await this.helper.callRpc(`api.query.${this.collective}.proposals`, [])).toHuman();3023 }30243025 30263027302830293030 async getProposalCallOf(hash: string) {3031 return (await this.helper.callRpc(`api.query.${this.collective}.proposalOf`, [hash])).toHuman();3032 }30333034 303530363037 async getTotalProposalsCount() {3038 return (await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, [])).toNumber();3039 }30403041 30423043304430453046304730483049 async propose(signer: TSigner, proposal: any, voteThreshold: number, lengthBound = 10000) {3050 return await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [voteThreshold, proposal, lengthBound]);3051 }30523053 30543055305630573058305930603061 vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {3062 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve]);3063 }30643065 3066306730683069307030713072 async execute(signer: TSigner, proposal: any, lengthBound = 10000) {3073 const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.execute`, [proposal, lengthBound]);3074 this.checkExecutedEvent(result.result.events);3075 return result;3076 }30773078 307930803081308230833084308530863087 async close(3088 signer: TSigner,3089 proposalHash: string,3090 proposalIndex: number,3091 weightBound: [number, number] | any = [20_000_000_000, 1000_000],3092 lengthBound = 10_000,3093 ) {3094 const result = await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [3095 proposalHash,3096 proposalIndex,3097 weightBound,3098 lengthBound,3099 ]);3100 this.checkExecutedEvent(result.result.events);3101 return result;3102 }31033104 310531063107310831093110 disapproveProposal(signer: TSigner, proposalHash: string) {3111 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.disapproveProposal`, [proposalHash]);3112 }3113}31143115class CollectiveMembershipGroup extends HelperGroup<UniqueHelper> {3116 311731183119 private membership: string;31203121 constructor(helper: UniqueHelper, membership: string) {3122 super(helper);3123 this.membership = membership;3124 }31253126 3127312831293130 async getMembers() {3131 return (await this.helper.callRpc(`api.query.${this.membership}.members`, [])).toHuman();3132 }31333134 313531363137 async getPrimeMember() {3138 return (await this.helper.callRpc(`api.query.${this.membership}.prime`, [])).toHuman();3139 }31403141 314231433144314531463147 addMember(signer: TSigner, member: string) {3148 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.addMember`, [member]);3149 }31503151 addMemberCall(member: string) {3152 return this.helper.constructApiCall(`api.tx.${this.membership}.addMember`, [member]);3153 }31543155 315631573158315931603161 removeMember(signer: TSigner, member: string) {3162 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.removeMember`, [member]);3163 }31643165 removeMemberCall(member: string) {3166 return this.helper.constructApiCall(`api.tx.${this.membership}.removeMember`, [member]);3167 }31683169 317031713172317331743175 resetMembers(signer: TSigner, members: string[]) {3176 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.resetMembers`, [members]);3177 }31783179 318031813182318331843185 setPrime(signer: TSigner, prime: string) {3186 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.setPrime`, [prime]);3187 }31883189 setPrimeCall(member: string) {3190 return this.helper.constructApiCall(`api.tx.${this.membership}.setPrime`, [member]);3191 }31923193 31943195319631973198 clearPrime(signer: TSigner) {3199 return this.helper.executeExtrinsic(signer, `api.tx.${this.membership}.clearPrime`, []);3200 }32013202 clearPrimeCall() {3203 return this.helper.constructApiCall(`api.tx.${this.membership}.clearPrime`, []);3204 }3205}32063207class RankedCollectiveGroup extends HelperGroup<UniqueHelper> {3208 320932103211 private collective: string;32123213 constructor(helper: UniqueHelper, collective: string) {3214 super(helper);3215 this.collective = collective;3216 }32173218 addMember(signer: TSigner, newMember: string) {3219 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.addMember`, [newMember]);3220 }32213222 addMemberCall(newMember: string) {3223 return this.helper.constructApiCall(`api.tx.${this.collective}.addMember`, [newMember]);3224 }32253226 removeMember(signer: TSigner, member: string, minRank: number) {3227 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.removeMember`, [member, minRank]);3228 }32293230 removeMemberCall(newMember: string, minRank: number) {3231 return this.helper.constructApiCall(`api.tx.${this.collective}.removeMember`, [newMember, minRank]);3232 }32333234 promote(signer: TSigner, member: string) {3235 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.promoteMember`, [member]);3236 }32373238 promoteCall(newMember: string) {3239 return this.helper.constructApiCall(`api.tx.${this.collective}.promoteMember`, [newMember]);3240 }32413242 demote(signer: TSigner, member: string) {3243 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.demoteMember`, [member]);3244 }32453246 demoteCall(newMember: string) {3247 return this.helper.constructApiCall(`api.tx.${this.collective}.demoteMember`, [newMember]);3248 }32493250 vote(signer: TSigner, pollIndex: number, aye: boolean) {3251 return this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [pollIndex, aye]);3252 }32533254 async getMembers() {3255 return (await this.helper.getApi().query.fellowshipCollective.members.keys())3256 .map((key) => key.args[0].toString());3257 }3258}32593260class ReferendaGroup extends HelperGroup<UniqueHelper> {3261 326232633264 private referenda: string;32653266 constructor(helper: UniqueHelper, referenda: string) {3267 super(helper);3268 this.referenda = referenda;3269 }32703271 submit(3272 signer: TSigner,3273 proposalOrigin: string,3274 proposal: any,3275 enactmentMoment: any,3276 ) {3277 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.submit`, [3278 {Origins: proposalOrigin},3279 proposal,3280 enactmentMoment,3281 ]);3282 }32833284 placeDecisionDeposit(signer: TSigner, referendumIndex: number) {3285 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.placeDecisionDeposit`, [referendumIndex]);3286 }32873288 cancel(signer: TSigner, referendumIndex: number) {3289 return this.helper.executeExtrinsic(signer, `api.tx.${this.referenda}.cancel`, [referendumIndex]);3290 }32913292 cancelCall(referendumIndex: number) {3293 return this.helper.constructApiCall(`api.tx.${this.referenda}.cancel`, [referendumIndex]);3294 }32953296 async referendumInfo(referendumIndex: number) {3297 return (await this.helper.callRpc(`api.query.${this.referenda}.referendumInfoFor`, [referendumIndex])).toJSON();3298 }32993300 async enactmentEventId(referendumIndex: number) {3301 const api = await this.helper.getApi();33023303 const bytes = api.createType('([u8;8], Text, u32)', ['assembly', 'enactment', referendumIndex]).toU8a();3304 return blake2AsHex(bytes, 256);3305 }3306}33073308export interface IFellowshipGroup {3309 collective: RankedCollectiveGroup;3310 referenda: ReferendaGroup;3311}33123313export interface ICollectiveGroup {3314 collective: CollectiveGroup;3315 membership: CollectiveMembershipGroup;3316}33173318class DemocracyGroup extends HelperGroup<UniqueHelper> {3319 3320 propose(signer: TSigner, call: any, deposit: bigint) {3321 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);3322 }33233324 proposeWithPreimage(signer: TSigner, preimage: string, deposit: bigint) {3325 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.propose', [{Legacy: preimage}, deposit]);3326 }33273328 proposeCall(call: any, deposit: bigint) {3329 return this.helper.constructApiCall('api.tx.democracy.propose', [{Inline: call.method.toHex()}, deposit]);3330 }33313332 second(signer: TSigner, proposalIndex: number) {3333 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.second', [proposalIndex]);3334 }33353336 externalPropose(signer: TSigner, proposalCall: any) {3337 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);3338 }33393340 externalProposeMajority(signer: TSigner, proposalCall: any) {3341 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);3342 }33433344 externalProposeDefault(signer: TSigner, proposalCall: any) {3345 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);3346 }33473348 externalProposeDefaultWithPreimage(signer: TSigner, preimage: string) {3349 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.externalProposeDefault', [{Legacy: preimage}]);3350 }33513352 externalProposeCall(proposalCall: any) {3353 return this.helper.constructApiCall('api.tx.democracy.externalPropose', [{Inline: proposalCall.method.toHex()}]);3354 }33553356 externalProposeMajorityCall(proposalCall: any) {3357 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [{Inline: proposalCall.method.toHex()}]);3358 }33593360 externalProposeDefaultCall(proposalCall: any) {3361 return this.helper.constructApiCall('api.tx.democracy.externalProposeDefault', [{Inline: proposalCall.method.toHex()}]);3362 }33633364 3365 vetoExternal(signer: TSigner, proposalHash: string) {3366 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vetoExternal', [proposalHash]);3367 }33683369 vetoExternalCall(proposalHash: string) {3370 return this.helper.constructApiCall('api.tx.democracy.vetoExternal', [proposalHash]);3371 }33723373 blacklist(signer: TSigner, proposalHash: string, referendumIndex: number | null = null) {3374 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.blacklist', [proposalHash, referendumIndex]);3375 }33763377 blacklistCall(proposalHash: string, referendumIndex: number | null = null) {3378 return this.helper.constructApiCall('api.tx.democracy.blacklist', [proposalHash, referendumIndex]);3379 }33803381 3382 cancelProposal(signer: TSigner, proposalIndex: number) {3383 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.cancelProposal', [proposalIndex]);3384 }33853386 cancelProposalCall(proposalIndex: number) {3387 return this.helper.constructApiCall('api.tx.democracy.cancelProposal', [proposalIndex]);3388 }33893390 clearPublicProposals(signer: TSigner) {3391 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.clearPublicProposals', []);3392 }33933394 fastTrack(signer: TSigner, proposalHash: string, votingPeriod: number, delayPeriod: number) {3395 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);3396 }33973398 fastTrackCall(proposalHash: string, votingPeriod: number, delayPeriod: number) {3399 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);3400 }34013402 3403 emergencyCancel(signer: TSigner, referendumIndex: number) {3404 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.emergencyCancel', [referendumIndex]);3405 }34063407 emergencyCancelCall(referendumIndex: number) {3408 return this.helper.constructApiCall('api.tx.democracy.emergencyCancel', [referendumIndex]);3409 }34103411 vote(signer: TSigner, referendumIndex: number, vote: any) {3412 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, vote]);3413 }34143415 removeVote(signer: TSigner, referendumIndex: number, targetAccount?: string) {3416 if(targetAccount) {3417 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeOtherVote', [targetAccount, referendumIndex]);3418 } else {3419 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.removeVote', [referendumIndex]);3420 }3421 }34223423 unlock(signer: TSigner, targetAccount: string) {3424 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.unlock', [targetAccount]);3425 }34263427 delegate(signer: TSigner, toAccount: string, conviction: PalletDemocracyConviction, balance: bigint) {3428 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.delegate', [toAccount, conviction, balance]);3429 }34303431 undelegate(signer: TSigner) {3432 return this.helper.executeExtrinsic(signer, 'api.tx.democracy.undelegate', []);3433 }34343435 async referendumInfo(referendumIndex: number) {3436 return (await this.helper.callRpc('api.query.democracy.referendumInfoOf', [referendumIndex])).toJSON();3437 }34383439 async publicProposals() {3440 return (await this.helper.callRpc('api.query.democracy.publicProps', [])).toJSON();3441 }34423443 async findPublicProposal(proposalIndex: number) {3444 const proposalInfo = (await this.publicProposals()).find((proposalInfo: any[]) => proposalInfo[0] == proposalIndex);34453446 return proposalInfo ? proposalInfo[1] : null;3447 }34483449 async expectPublicProposal(proposalIndex: number) {3450 const proposal = await this.findPublicProposal(proposalIndex);34513452 if(proposal) {3453 return proposal;3454 } else {3455 throw Error(`Proposal #${proposalIndex} is expected to exist`);3456 }3457 }34583459 async getExternalProposal() {3460 return (await this.helper.callRpc('api.query.democracy.nextExternal', []));3461 }34623463 async expectExternalProposal() {3464 const proposal = await this.getExternalProposal();34653466 if(proposal) {3467 return proposal;3468 } else {3469 throw Error('An external proposal is expected to exist');3470 }3471 }34723473 34743475 3476347734783479}34803481class PreimageGroup extends HelperGroup<UniqueHelper> {3482 async getPreimageInfo(h256: string) {3483 return (await this.helper.callRpc('api.query.preimage.statusFor', [h256])).toJSON();3484 }34853486 348734883489349034913492349334943495 notePreimageFromCall(signer: TSigner, call: any, returnPreimageHash = false) {3496 return this.notePreimage(signer, call.method.toHex(), returnPreimageHash);3497 }34983499 350035013502350335043505350635073508 async notePreimage(signer: TSigner, bytes: string | Uint8Array, returnPreimageHash = false) {3509 const promise = this.helper.executeExtrinsic(signer, 'api.tx.preimage.notePreimage', [bytes]);3510 if(returnPreimageHash) {3511 const result = await promise;3512 const events = result.result.events.filter(x => x.event.method === 'Noted' && x.event.section === 'preimage');3513 const preimageHash = events[0].event.data[0].toHuman();3514 return preimageHash;3515 }3516 return promise;3517 }35183519 352035213522352335243525 unnotePreimage(signer: TSigner, h256: string) {3526 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unnotePreimage', [h256]);3527 }35283529 353035313532353335343535 requestPreimage(signer: TSigner, h256: string) {3536 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.requestPreimage', [h256]);3537 }35383539 354035413542354335443545 unrequestPreimage(signer: TSigner, h256: string) {3546 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unrequestPreimage', [h256]);3547 }3548}35493550class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {3551 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {3552 await this.helper.executeExtrinsic(3553 signer,3554 'api.tx.foreignAssets.registerForeignAsset',3555 [ownerAddress, location, metadata],3556 true,3557 );3558 }35593560 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {3561 await this.helper.executeExtrinsic(3562 signer,3563 'api.tx.foreignAssets.updateForeignAsset',3564 [foreignAssetId, location, metadata],3565 true,3566 );3567 }3568}35693570class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {3571 palletName: string;35723573 constructor(helper: T, palletName: string) {3574 super(helper);35753576 this.palletName = palletName;3577 }35783579 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: any) {3580 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, weightLimit], true);3581 }35823583 async setSafeXcmVersion(signer: TSigner, version: number) {3584 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.forceDefaultXcmVersion`, [version], true);3585 }35863587 async teleportAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number) {3588 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.teleportAssets`, [destination, beneficiary, assets, feeAssetItem], true);3589 }35903591 async teleportNativeAsset(signer: TSigner, destinationParaId: number, targetAccount: Uint8Array, amount: bigint, xcmVersion = 3) {3592 const destinationContent = {3593 parents: 0,3594 interior: {3595 X1: {3596 Parachain: destinationParaId,3597 },3598 },3599 };36003601 const beneficiaryContent = {3602 parents: 0,3603 interior: {3604 X1: {3605 AccountId32: {3606 network: 'Any',3607 id: targetAccount,3608 },3609 },3610 },3611 };36123613 const assetsContent = [3614 {3615 id: {3616 Concrete: {3617 parents: 0,3618 interior: 'Here',3619 },3620 },3621 fun: {3622 Fungible: amount,3623 },3624 },3625 ];36263627 let destination;3628 let beneficiary;3629 let assets;36303631 if(xcmVersion == 2) {3632 destination = {V1: destinationContent};3633 beneficiary = {V1: beneficiaryContent};3634 assets = {V1: assetsContent};36353636 } else if(xcmVersion == 3) {3637 destination = {V2: destinationContent};3638 beneficiary = {V2: beneficiaryContent};3639 assets = {V2: assetsContent};36403641 } else {3642 throw Error('Unknown XCM version: ' + xcmVersion);3643 }36443645 const feeAssetItem = 0;36463647 await this.teleportAssets(signer, destination, beneficiary, assets, feeAssetItem);3648 }36493650 async send(signer: IKeyringPair, destination: any, message: any) {3651 await this.helper.executeExtrinsic(3652 signer,3653 `api.tx.${this.palletName}.send`,3654 [3655 destination,3656 message,3657 ],3658 true,3659 );3660 }3661}36623663class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {3664 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: any) {3665 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);3666 }36673668 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: any) {3669 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);3670 }36713672 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: any) {3673 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);3674 }3675}36763677class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {3678 async accounts(address: string, currencyId: any) {3679 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;3680 return BigInt(free);3681 }3682}36833684class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {3685 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {3686 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);3687 }36883689 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {3690 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);3691 }36923693 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {3694 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);3695 }36963697 async account(assetId: string | number, address: string) {3698 const accountAsset = (3699 await this.helper.callRpc('api.query.assets.account', [assetId, address])3700 ).toJSON()! as any;37013702 if(accountAsset !== null) {3703 return BigInt(accountAsset['balance']);3704 } else {3705 return null;3706 }3707 }3708}37093710class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {3711 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {3712 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);3713 }3714}37153716class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {3717 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {3718 const apiPrefix = 'api.tx.assetManager.';37193720 const registerTx = this.helper.constructApiCall(3721 apiPrefix + 'registerForeignAsset',3722 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],3723 );37243725 const setUnitsTx = this.helper.constructApiCall(3726 apiPrefix + 'setAssetUnitsPerSecond',3727 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],3728 );37293730 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);3731 const encodedProposal = batchCall?.method.toHex() || '';3732 return encodedProposal;3733 }37343735 async assetTypeId(location: any) {3736 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);3737 }3738}37393740class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {3741 notePreimagePallet: string;37423743 constructor(helper: MoonbeamHelper, options: { [key: string]: any } = {}) {3744 super(helper);3745 this.notePreimagePallet = options.notePreimagePallet;3746 }37473748 async notePreimage(signer: TSigner, encodedProposal: string) {3749 await this.helper.executeExtrinsic(signer, `api.tx.${this.notePreimagePallet}.notePreimage`, [encodedProposal], true);3750 }37513752 externalProposeMajority(proposal: any) {3753 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposal]);3754 }37553756 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {3757 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);3758 }37593760 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {3761 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);3762 }3763}37643765class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {3766 collective: string;37673768 constructor(helper: MoonbeamHelper, collective: string) {3769 super(helper);37703771 this.collective = collective;3772 }37733774 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {3775 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);3776 }37773778 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {3779 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);3780 }37813782 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: any, lengthBound: number) {3783 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);3784 }37853786 async proposalCount() {3787 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));3788 }3789}37903791export type ChainHelperBaseConstructor = new (...args: any[]) => ChainHelperBase;3792export type UniqueHelperConstructor = new (...args: any[]) => UniqueHelper;37933794export class UniqueHelper extends ChainHelperBase {3795 balance: BalanceGroup<UniqueHelper>;3796 collection: CollectionGroup;3797 nft: NFTGroup;3798 rft: RFTGroup;3799 ft: FTGroup;3800 staking: StakingGroup;3801 scheduler: SchedulerGroup;3802 collatorSelection: CollatorSelectionGroup;3803 council: ICollectiveGroup;3804 technicalCommittee: ICollectiveGroup;3805 fellowship: IFellowshipGroup;3806 democracy: DemocracyGroup;3807 preimage: PreimageGroup;3808 foreignAssets: ForeignAssetsGroup;3809 xcm: XcmGroup<UniqueHelper>;3810 xTokens: XTokensGroup<UniqueHelper>;3811 tokens: TokensGroup<UniqueHelper>;38123813 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3814 super(logger, options.helperBase ?? UniqueHelper);38153816 this.balance = new BalanceGroup(this);3817 this.collection = new CollectionGroup(this);3818 this.nft = new NFTGroup(this);3819 this.rft = new RFTGroup(this);3820 this.ft = new FTGroup(this);3821 this.staking = new StakingGroup(this);3822 this.scheduler = new SchedulerGroup(this);3823 this.collatorSelection = new CollatorSelectionGroup(this);3824 this.council = {3825 collective: new CollectiveGroup(this, 'council'),3826 membership: new CollectiveMembershipGroup(this, 'councilMembership'),3827 };3828 this.technicalCommittee = {3829 collective: new CollectiveGroup(this, 'technicalCommittee'),3830 membership: new CollectiveMembershipGroup(this, 'technicalCommitteeMembership'),3831 };3832 this.fellowship = {3833 collective: new RankedCollectiveGroup(this, 'fellowshipCollective'),3834 referenda: new ReferendaGroup(this, 'fellowshipReferenda'),3835 };3836 this.democracy = new DemocracyGroup(this);3837 this.preimage = new PreimageGroup(this);3838 this.foreignAssets = new ForeignAssetsGroup(this);3839 this.xcm = new XcmGroup(this, 'polkadotXcm');3840 this.xTokens = new XTokensGroup(this);3841 this.tokens = new TokensGroup(this);3842 }38433844 getSudo<T extends UniqueHelper>() {3845 3846 const SudoHelperType = SudoHelper(this.helperBase);3847 return this.clone(SudoHelperType) as T;3848 }3849}38503851export class XcmChainHelper extends ChainHelperBase {3852 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {3853 const wsProvider = new WsProvider(wsEndpoint);3854 this.api = new ApiPromise({3855 provider: wsProvider,3856 });3857 await this.api.isReadyOrError;3858 this.network = await UniqueHelper.detectNetwork(this.api);3859 }3860}38613862export class RelayHelper extends XcmChainHelper {3863 balance: SubstrateBalanceGroup<RelayHelper>;3864 xcm: XcmGroup<RelayHelper>;38653866 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3867 super(logger, options.helperBase ?? RelayHelper);38683869 this.balance = new SubstrateBalanceGroup(this);3870 this.xcm = new XcmGroup(this, 'xcmPallet');3871 }3872}38733874export class WestmintHelper extends XcmChainHelper {3875 balance: SubstrateBalanceGroup<WestmintHelper>;3876 xcm: XcmGroup<WestmintHelper>;3877 assets: AssetsGroup<WestmintHelper>;3878 xTokens: XTokensGroup<WestmintHelper>;38793880 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3881 super(logger, options.helperBase ?? WestmintHelper);38823883 this.balance = new SubstrateBalanceGroup(this);3884 this.xcm = new XcmGroup(this, 'polkadotXcm');3885 this.assets = new AssetsGroup(this);3886 this.xTokens = new XTokensGroup(this);3887 }3888}38893890export class MoonbeamHelper extends XcmChainHelper {3891 balance: EthereumBalanceGroup<MoonbeamHelper>;3892 assetManager: MoonbeamAssetManagerGroup;3893 assets: AssetsGroup<MoonbeamHelper>;3894 xTokens: XTokensGroup<MoonbeamHelper>;3895 democracy: MoonbeamDemocracyGroup;3896 collective: {3897 council: MoonbeamCollectiveGroup,3898 techCommittee: MoonbeamCollectiveGroup,3899 };39003901 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3902 super(logger, options.helperBase ?? MoonbeamHelper);39033904 this.balance = new EthereumBalanceGroup(this);3905 this.assetManager = new MoonbeamAssetManagerGroup(this);3906 this.assets = new AssetsGroup(this);3907 this.xTokens = new XTokensGroup(this);3908 this.democracy = new MoonbeamDemocracyGroup(this, options);3909 this.collective = {3910 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),3911 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),3912 };3913 }3914}39153916export class AstarHelper extends XcmChainHelper {3917 balance: SubstrateBalanceGroup<AstarHelper>;3918 assets: AssetsGroup<AstarHelper>;3919 xcm: XcmGroup<AstarHelper>;39203921 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3922 super(logger, options.helperBase ?? AstarHelper);39233924 this.balance = new SubstrateBalanceGroup(this);3925 this.assets = new AssetsGroup(this);3926 this.xcm = new XcmGroup(this, 'polkadotXcm');3927 }39283929 getSudo<T extends UniqueHelper>() {3930 3931 const SudoHelperType = SudoHelper(this.helperBase);3932 return this.clone(SudoHelperType) as T;3933 }3934}39353936export class AcalaHelper extends XcmChainHelper {3937 balance: SubstrateBalanceGroup<AcalaHelper>;3938 assetRegistry: AcalaAssetRegistryGroup;3939 xTokens: XTokensGroup<AcalaHelper>;3940 tokens: TokensGroup<AcalaHelper>;3941 xcm: XcmGroup<AcalaHelper>;39423943 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {3944 super(logger, options.helperBase ?? AcalaHelper);39453946 this.balance = new SubstrateBalanceGroup(this);3947 this.assetRegistry = new AcalaAssetRegistryGroup(this);3948 this.xTokens = new XTokensGroup(this);3949 this.tokens = new TokensGroup(this);3950 this.xcm = new XcmGroup(this, 'polkadotXcm');3951 }39523953 getSudo<T extends AcalaHelper>() {3954 3955 const SudoHelperType = SudoHelper(this.helperBase);3956 return this.clone(SudoHelperType) as T;3957 }3958}395939603961function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {3962 return class extends Base {3963 scheduleFn: 'schedule' | 'scheduleAfter';3964 blocksNum: number;3965 options: ISchedulerOptions;39663967 constructor(...args: any[]) {3968 const logger = args[0] as ILogger;3969 const options = args[1] as {3970 scheduleFn: 'schedule' | 'scheduleAfter',3971 blocksNum: number,3972 options: ISchedulerOptions3973 };39743975 super(logger);39763977 this.scheduleFn = options.scheduleFn;3978 this.blocksNum = options.blocksNum;3979 this.options = options.options;3980 }39813982 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {3983 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);39843985 const mandatorySchedArgs = [3986 this.blocksNum,3987 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,3988 this.options.priority ?? null,3989 scheduledTx,3990 ];39913992 let schedArgs;3993 let scheduleFn;39943995 if(this.options.scheduledId) {3996 schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];39973998 if(this.scheduleFn == 'schedule') {3999 scheduleFn = 'scheduleNamed';4000 } else if(this.scheduleFn == 'scheduleAfter') {4001 scheduleFn = 'scheduleNamedAfter';4002 }4003 } else {4004 schedArgs = mandatorySchedArgs;4005 scheduleFn = this.scheduleFn;4006 }40074008 const extrinsic = 'api.tx.scheduler.' + scheduleFn;40094010 return super.executeExtrinsic(4011 sender,4012 extrinsic as any,4013 schedArgs,4014 expectSuccess,4015 );4016 }4017 };4018}401940204021function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {4022 return class extends Base {4023 constructor(...args: any[]) {4024 super(...args);4025 }40264027 async executeExtrinsic(4028 sender: IKeyringPair,4029 extrinsic: string,4030 params: any[],4031 expectSuccess?: boolean,4032 options: Partial<SignerOptions> | null = null,4033 ): Promise<ITransactionResult> {4034 const call = this.constructApiCall(extrinsic, params);4035 const result = await super.executeExtrinsic(4036 sender,4037 'api.tx.sudo.sudo',4038 [call],4039 expectSuccess,4040 options,4041 );40424043 if(result.status === 'Fail') return result;40444045 const data = (result.result.events.find(x => x.event.section == 'sudo' && x.event.method == 'Sudid')?.event.data as any).sudoResult;4046 if(data.isErr) {4047 if(data.asErr.isModule) {4048 const error = (result.result.events[1].event.data as any).sudoResult.asErr.asModule;4049 const metaError = super.getApi()?.registry.findMetaError(error);4050 throw new Error(`${metaError.section}.${metaError.name}`);4051 } else if(data.asErr.isToken) {4052 throw new Error(`Token: ${data.asErr.asToken}`);4053 }4054 4055 throw new Error(`Misc: ${data.asErr.toHuman()}`);4056 }4057 return result;4058 }4059 };4060}40614062export class UniqueBaseCollection {4063 helper: UniqueHelper;4064 collectionId: number;40654066 constructor(collectionId: number, uniqueHelper: UniqueHelper) {4067 this.collectionId = collectionId;4068 this.helper = uniqueHelper;4069 }40704071 async getData() {4072 return await this.helper.collection.getData(this.collectionId);4073 }40744075 async getLastTokenId() {4076 return await this.helper.collection.getLastTokenId(this.collectionId);4077 }40784079 async doesTokenExist(tokenId: number) {4080 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);4081 }40824083 async getAdmins() {4084 return await this.helper.collection.getAdmins(this.collectionId);4085 }40864087 async getAllowList() {4088 return await this.helper.collection.getAllowList(this.collectionId);4089 }40904091 async getEffectiveLimits() {4092 return await this.helper.collection.getEffectiveLimits(this.collectionId);4093 }40944095 async getProperties(propertyKeys?: string[] | null) {4096 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);4097 }40984099 async getPropertiesConsumedSpace() {4100 return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);4101 }41024103 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {4104 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);4105 }41064107 async getOptions() {4108 return await this.helper.collection.getCollectionOptions(this.collectionId);4109 }41104111 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {4112 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);4113 }41144115 async confirmSponsorship(signer: TSigner) {4116 return await this.helper.collection.confirmSponsorship(signer, this.collectionId);4117 }41184119 async removeSponsor(signer: TSigner) {4120 return await this.helper.collection.removeSponsor(signer, this.collectionId);4121 }41224123 async setLimits(signer: TSigner, limits: ICollectionLimits) {4124 return await this.helper.collection.setLimits(signer, this.collectionId, limits);4125 }41264127 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {4128 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);4129 }41304131 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {4132 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);4133 }41344135 async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {4136 return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);4137 }41384139 async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {4140 return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);4141 }41424143 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {4144 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);4145 }41464147 async setProperties(signer: TSigner, properties: IProperty[]) {4148 return await this.helper.collection.setProperties(signer, this.collectionId, properties);4149 }41504151 async deleteProperties(signer: TSigner, propertyKeys: string[]) {4152 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);4153 }41544155 async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {4156 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);4157 }41584159 async enableNesting(signer: TSigner, permissions: INestingPermissions) {4160 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);4161 }41624163 async disableNesting(signer: TSigner) {4164 return await this.helper.collection.disableNesting(signer, this.collectionId);4165 }41664167 async burn(signer: TSigner) {4168 return await this.helper.collection.burn(signer, this.collectionId);4169 }41704171 scheduleAt<T extends UniqueHelper>(4172 executionBlockNumber: number,4173 options: ISchedulerOptions = {},4174 ) {4175 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);4176 return new UniqueBaseCollection(this.collectionId, scheduledHelper);4177 }41784179 scheduleAfter<T extends UniqueHelper>(4180 blocksBeforeExecution: number,4181 options: ISchedulerOptions = {},4182 ) {4183 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);4184 return new UniqueBaseCollection(this.collectionId, scheduledHelper);4185 }41864187 getSudo<T extends UniqueHelper>() {4188 return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());4189 }4190}419141924193export class UniqueNFTCollection extends UniqueBaseCollection {4194 getTokenObject(tokenId: number) {4195 return new UniqueNFToken(tokenId, this);4196 }41974198 async getTokensByAddress(addressObj: ICrossAccountId) {4199 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);4200 }42014202 async getToken(tokenId: number, blockHashAt?: string) {4203 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);4204 }42054206 async getTokenOwner(tokenId: number, blockHashAt?: string) {4207 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);4208 }42094210 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {4211 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);4212 }42134214 async getTokenChildren(tokenId: number, blockHashAt?: string) {4215 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);4216 }42174218 async getPropertyPermissions(propertyKeys: string[] | null = null) {4219 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);4220 }42214222 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {4223 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);4224 }42254226 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {4227 const api = this.helper.getApi();4228 const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON();42294230 return (props! as any).consumedSpace;4231 }42324233 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {4234 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);4235 }42364237 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {4238 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);4239 }42404241 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {4242 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);4243 }42444245 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {4246 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);4247 }42484249 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {4250 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});4251 }42524253 async mintMultipleTokens(signer: TSigner, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]) {4254 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);4255 }42564257 async burnToken(signer: TSigner, tokenId: number) {4258 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);4259 }42604261 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {4262 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);4263 }42644265 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {4266 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);4267 }42684269 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {4270 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);4271 }42724273 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {4274 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);4275 }42764277 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {4278 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);4279 }42804281 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {4282 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);4283 }42844285 scheduleAt<T extends UniqueHelper>(4286 executionBlockNumber: number,4287 options: ISchedulerOptions = {},4288 ) {4289 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);4290 return new UniqueNFTCollection(this.collectionId, scheduledHelper);4291 }42924293 scheduleAfter<T extends UniqueHelper>(4294 blocksBeforeExecution: number,4295 options: ISchedulerOptions = {},4296 ) {4297 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);4298 return new UniqueNFTCollection(this.collectionId, scheduledHelper);4299 }43004301 getSudo<T extends UniqueHelper>() {4302 return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());4303 }4304}430543064307export class UniqueRFTCollection extends UniqueBaseCollection {4308 getTokenObject(tokenId: number) {4309 return new UniqueRFToken(tokenId, this);4310 }43114312 async getToken(tokenId: number, blockHashAt?: string) {4313 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);4314 }43154316 async getTokenOwner(tokenId: number, blockHashAt?: string) {4317 return await this.helper.rft.getTokenOwner(this.collectionId, tokenId, blockHashAt);4318 }43194320 async getTokensByAddress(addressObj: ICrossAccountId) {4321 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);4322 }43234324 async getTop10TokenOwners(tokenId: number) {4325 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);4326 }43274328 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {4329 return await this.helper.rft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);4330 }43314332 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {4333 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);4334 }43354336 async getTokenTotalPieces(tokenId: number) {4337 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);4338 }43394340 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {4341 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);4342 }43434344 async getPropertyPermissions(propertyKeys: string[] | null = null) {4345 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);4346 }43474348 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {4349 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);4350 }43514352 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {4353 const api = this.helper.getApi();4354 const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON();43554356 return (props! as any).consumedSpace;4357 }43584359 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount = 1n) {4360 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);4361 }43624363 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {4364 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);4365 }43664367 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {4368 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);4369 }43704371 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {4372 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);4373 }43744375 async mintToken(signer: TSigner, pieces = 1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {4376 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});4377 }43784379 async mintMultipleTokens(signer: TSigner, tokens: { pieces: bigint, owner: ICrossAccountId, properties?: IProperty[] }[]) {4380 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);4381 }43824383 async burnToken(signer: TSigner, tokenId: number, amount = 1n) {4384 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);4385 }43864387 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n) {4388 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);4389 }43904391 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {4392 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);4393 }43944395 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {4396 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);4397 }43984399 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {4400 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);4401 }44024403 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {4404 return await this.helper.rft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);4405 }44064407 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {4408 return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);4409 }44104411 scheduleAt<T extends UniqueHelper>(4412 executionBlockNumber: number,4413 options: ISchedulerOptions = {},4414 ) {4415 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);4416 return new UniqueRFTCollection(this.collectionId, scheduledHelper);4417 }44184419 scheduleAfter<T extends UniqueHelper>(4420 blocksBeforeExecution: number,4421 options: ISchedulerOptions = {},4422 ) {4423 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);4424 return new UniqueRFTCollection(this.collectionId, scheduledHelper);4425 }44264427 getSudo<T extends UniqueHelper>() {4428 return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());4429 }4430}443144324433export class UniqueFTCollection extends UniqueBaseCollection {4434 async getBalance(addressObj: ICrossAccountId) {4435 return await this.helper.ft.getBalance(this.collectionId, addressObj);4436 }44374438 async getTotalPieces() {4439 return await this.helper.ft.getTotalPieces(this.collectionId);4440 }44414442 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {4443 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);4444 }44454446 async getTop10Owners() {4447 return await this.helper.ft.getTop10Owners(this.collectionId);4448 }44494450 async mint(signer: TSigner, amount = 1n, owner: ICrossAccountId = {Substrate: signer.address}) {4451 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);4452 }44534454 async mintWithOneOwner(signer: TSigner, tokens: { value: bigint }[], owner: ICrossAccountId = {Substrate: signer.address}) {4455 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);4456 }44574458 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {4459 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);4460 }44614462 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {4463 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);4464 }44654466 async burnTokens(signer: TSigner, amount = 1n) {4467 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);4468 }44694470 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {4471 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);4472 }44734474 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {4475 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);4476 }44774478 scheduleAt<T extends UniqueHelper>(4479 executionBlockNumber: number,4480 options: ISchedulerOptions = {},4481 ) {4482 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);4483 return new UniqueFTCollection(this.collectionId, scheduledHelper);4484 }44854486 scheduleAfter<T extends UniqueHelper>(4487 blocksBeforeExecution: number,4488 options: ISchedulerOptions = {},4489 ) {4490 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);4491 return new UniqueFTCollection(this.collectionId, scheduledHelper);4492 }44934494 getSudo<T extends UniqueHelper>() {4495 return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());4496 }4497}449844994500export class UniqueBaseToken {4501 collection: UniqueNFTCollection | UniqueRFTCollection;4502 collectionId: number;4503 tokenId: number;45044505 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {4506 this.collection = collection;4507 this.collectionId = collection.collectionId;4508 this.tokenId = tokenId;4509 }45104511 async getNextSponsored(addressObj: ICrossAccountId) {4512 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);4513 }45144515 async getProperties(propertyKeys?: string[] | null) {4516 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);4517 }45184519 async getTokenPropertiesConsumedSpace() {4520 return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);4521 }45224523 async setProperties(signer: TSigner, properties: IProperty[]) {4524 return await this.collection.setTokenProperties(signer, this.tokenId, properties);4525 }45264527 async deleteProperties(signer: TSigner, propertyKeys: string[]) {4528 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);4529 }45304531 async doesExist() {4532 return await this.collection.doesTokenExist(this.tokenId);4533 }45344535 nestingAccount() {4536 return this.collection.helper.util.getTokenAccount(this);4537 }45384539 scheduleAt<T extends UniqueHelper>(4540 executionBlockNumber: number,4541 options: ISchedulerOptions = {},4542 ) {4543 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);4544 return new UniqueBaseToken(this.tokenId, scheduledCollection);4545 }45464547 scheduleAfter<T extends UniqueHelper>(4548 blocksBeforeExecution: number,4549 options: ISchedulerOptions = {},4550 ) {4551 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);4552 return new UniqueBaseToken(this.tokenId, scheduledCollection);4553 }45544555 getSudo<T extends UniqueHelper>() {4556 return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());4557 }4558}455945604561export class UniqueNFToken extends UniqueBaseToken {4562 collection: UniqueNFTCollection;45634564 constructor(tokenId: number, collection: UniqueNFTCollection) {4565 super(tokenId, collection);4566 this.collection = collection;4567 }45684569 async getData(blockHashAt?: string) {4570 return await this.collection.getToken(this.tokenId, blockHashAt);4571 }45724573 async getOwner(blockHashAt?: string) {4574 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);4575 }45764577 async getTopmostOwner(blockHashAt?: string) {4578 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);4579 }45804581 async getChildren(blockHashAt?: string) {4582 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);4583 }45844585 async nest(signer: TSigner, toTokenObj: IToken) {4586 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);4587 }45884589 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {4590 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);4591 }45924593 async transfer(signer: TSigner, addressObj: ICrossAccountId) {4594 return await this.collection.transferToken(signer, this.tokenId, addressObj);4595 }45964597 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {4598 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);4599 }46004601 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {4602 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);4603 }46044605 async isApproved(toAddressObj: ICrossAccountId) {4606 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);4607 }46084609 async burn(signer: TSigner) {4610 return await this.collection.burnToken(signer, this.tokenId);4611 }46124613 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {4614 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);4615 }46164617 scheduleAt<T extends UniqueHelper>(4618 executionBlockNumber: number,4619 options: ISchedulerOptions = {},4620 ) {4621 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);4622 return new UniqueNFToken(this.tokenId, scheduledCollection);4623 }46244625 scheduleAfter<T extends UniqueHelper>(4626 blocksBeforeExecution: number,4627 options: ISchedulerOptions = {},4628 ) {4629 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);4630 return new UniqueNFToken(this.tokenId, scheduledCollection);4631 }46324633 getSudo<T extends UniqueHelper>() {4634 return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());4635 }4636}46374638export class UniqueRFToken extends UniqueBaseToken {4639 collection: UniqueRFTCollection;46404641 constructor(tokenId: number, collection: UniqueRFTCollection) {4642 super(tokenId, collection);4643 this.collection = collection;4644 }46454646 async getData(blockHashAt?: string) {4647 return await this.collection.getToken(this.tokenId, blockHashAt);4648 }46494650 async getOwner(blockHashAt?: string) {4651 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);4652 }46534654 async getTop10Owners() {4655 return await this.collection.getTop10TokenOwners(this.tokenId);4656 }46574658 async getTopmostOwner(blockHashAt?: string) {4659 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);4660 }46614662 async nest(signer: TSigner, toTokenObj: IToken) {4663 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);4664 }46654666 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {4667 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);4668 }46694670 async getBalance(addressObj: ICrossAccountId) {4671 return await this.collection.getTokenBalance(this.tokenId, addressObj);4672 }46734674 async getTotalPieces() {4675 return await this.collection.getTokenTotalPieces(this.tokenId);4676 }46774678 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {4679 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);4680 }46814682 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount = 1n) {4683 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);4684 }46854686 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {4687 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);4688 }46894690 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {4691 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);4692 }46934694 async repartition(signer: TSigner, amount: bigint) {4695 return await this.collection.repartitionToken(signer, this.tokenId, amount);4696 }46974698 async burn(signer: TSigner, amount = 1n) {4699 return await this.collection.burnToken(signer, this.tokenId, amount);4700 }47014702 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {4703 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);4704 }47054706 scheduleAt<T extends UniqueHelper>(4707 executionBlockNumber: number,4708 options: ISchedulerOptions = {},4709 ) {4710 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);4711 return new UniqueRFToken(this.tokenId, scheduledCollection);4712 }47134714 scheduleAfter<T extends UniqueHelper>(4715 blocksBeforeExecution: number,4716 options: ISchedulerOptions = {},4717 ) {4718 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);4719 return new UniqueRFToken(this.tokenId, scheduledCollection);4720 }47214722 getSudo<T extends UniqueHelper>() {4723 return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());4724 }4725}