12345678import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';12import {hexToU8a} from '@polkadot/util/hex';13import {u8aConcat} from '@polkadot/util/u8a';14import {15 IApiListeners,16 IBlock,17 IEvent,18 IChainProperties,19 ICollectionCreationOptions,20 ICollectionLimits,21 ICollectionPermissions,22 ICrossAccountId,23 ICrossAccountIdLower,24 ILogger,25 INestingPermissions,26 IProperty,27 IStakingInfo,28 ISchedulerOptions,29 ISubstrateBalance,30 IToken,31 ITokenPropertyPermission,32 ITransactionResult,33 IUniqueHelperLog,34 TApiAllowedListeners,35 TEthereumAccount,36 TSigner,37 TSubstrateAccount,38 TNetworks,39 IForeignAssetMetadata,40 AcalaAssetMetadata,41 MoonbeamAssetInfo,42 DemocracyStandardAccountVote,43 IEthCrossAccountId,44} from './types';45import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';46import type {Vec} from '@polkadot/types-codec';47import { FrameSystemEventRecord } from '@polkadot/types/lookup';4849export class CrossAccountId implements ICrossAccountId {50 Substrate?: TSubstrateAccount;51 Ethereum?: TEthereumAccount;5253 constructor(account: ICrossAccountId) {54 if (account.Substrate) this.Substrate = account.Substrate;55 if (account.Ethereum) this.Ethereum = account.Ethereum;56 }5758 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {59 switch (domain) {60 case 'Substrate': return new CrossAccountId({Substrate: account.address});61 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();62 }63 }6465 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {66 return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});67 }6869 static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {70 return encodeAddress(decodeAddress(address), ss58Format);71 }7273 static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {74 return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});75 }7677 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {78 if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);79 return this;80 }8182 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {83 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));84 }8586 toEthereum(): CrossAccountId {87 if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});88 return this;89 }9091 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {92 return evmToAddress(address, ss58Format);93 }9495 toSubstrate(ss58Format?: number): CrossAccountId {96 if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});97 return this;98 }99100 toLowerCase(): CrossAccountId {101 if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();102 if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();103 return this;104 }105}106107const nesting = {108 toChecksumAddress(address: string): string {109 if (typeof address === 'undefined') return '';110111 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);112113 address = address.toLowerCase().replace(/^0x/i,'');114 const addressHash = keccakAsHex(address).replace(/^0x/i,'');115 const checksumAddress = ['0x'];116117 for (let i = 0; i < address.length; i++) {118 119 if (parseInt(addressHash[i], 16) > 7) {120 checksumAddress.push(address[i].toUpperCase());121 } else {122 checksumAddress.push(address[i]);123 }124 }125 return checksumAddress.join('');126 },127 tokenIdToAddress(collectionId: number, tokenId: number) {128 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);129 },130};131132class UniqueUtil {133 static transactionStatus = {134 NOT_READY: 'NotReady',135 FAIL: 'Fail',136 SUCCESS: 'Success',137 };138139 static chainLogType = {140 EXTRINSIC: 'extrinsic',141 RPC: 'rpc',142 };143144 static getTokenAccount(token: IToken): CrossAccountId {145 return new CrossAccountId({Ethereum: this.getTokenAddress(token)});146 }147148 static getTokenAddress(token: IToken): string {149 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);150 }151152 static getDefaultLogger(): ILogger {153 return {154 log(msg: any, level = 'INFO') {155 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));156 },157 level: {158 ERROR: 'ERROR',159 WARNING: 'WARNING',160 INFO: 'INFO',161 },162 };163 }164165 static vec2str(arr: string[] | number[]) {166 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');167 }168169 static str2vec(string: string) {170 if (typeof string !== 'string') return string;171 return Array.from(string).map(x => x.charCodeAt(0));172 }173174 static fromSeed(seed: string, ss58Format = 42) {175 const keyring = new Keyring({type: 'sr25519', ss58Format});176 return keyring.addFromUri(seed);177 }178179 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {180 if (creationResult.status !== this.transactionStatus.SUCCESS) {181 throw Error('Unable to create collection!');182 }183184 let collectionId = null;185 creationResult.result.events.forEach(({event: {data, method, section}}) => {186 if ((section === 'common') && (method === 'CollectionCreated')) {187 collectionId = parseInt(data[0].toString(), 10);188 }189 });190191 if (collectionId === null) {192 throw Error('No CollectionCreated event was found!');193 }194195 return collectionId;196 }197198 static extractTokensFromCreationResult(creationResult: ITransactionResult): {199 success: boolean,200 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],201 } {202 if (creationResult.status !== this.transactionStatus.SUCCESS) {203 throw Error('Unable to create tokens!');204 }205 let success = false;206 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];207 creationResult.result.events.forEach(({event: {data, method, section}}) => {208 if (method === 'ExtrinsicSuccess') {209 success = true;210 } else if ((section === 'common') && (method === 'ItemCreated')) {211 tokens.push({212 collectionId: parseInt(data[0].toString(), 10),213 tokenId: parseInt(data[1].toString(), 10),214 owner: data[2].toHuman(),215 amount: data[3].toBigInt(),216 });217 }218 });219 return {success, tokens};220 }221222 static extractTokensFromBurnResult(burnResult: ITransactionResult): {223 success: boolean,224 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],225 } {226 if (burnResult.status !== this.transactionStatus.SUCCESS) {227 throw Error('Unable to burn tokens!');228 }229 let success = false;230 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];231 burnResult.result.events.forEach(({event: {data, method, section}}) => {232 if (method === 'ExtrinsicSuccess') {233 success = true;234 } else if ((section === 'common') && (method === 'ItemDestroyed')) {235 tokens.push({236 collectionId: parseInt(data[0].toString(), 10),237 tokenId: parseInt(data[1].toString(), 10),238 owner: data[2].toHuman(),239 amount: data[3].toBigInt(),240 });241 }242 });243 return {success, tokens};244 }245246 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {247 let eventId = null;248 events.forEach(({event: {data, method, section}}) => {249 if ((section === expectedSection) && (method === expectedMethod)) {250 eventId = parseInt(data[0].toString(), 10);251 }252 });253254 if (eventId === null) {255 throw Error(`No ${expectedMethod} event was found!`);256 }257 return eventId === collectionId;258 }259260 static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {261 const normalizeAddress = (address: string | ICrossAccountId) => {262 if(typeof address === 'string') return address;263 const obj = {} as any;264 Object.keys(address).forEach(k => {265 obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];266 });267 if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);268 if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();269 return address;270 };271 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;272 events.forEach(({event: {data, method, section}}) => {273 if ((section === 'common') && (method === 'Transfer')) {274 const hData = (data as any).toJSON();275 transfer = {276 collectionId: hData[0],277 tokenId: hData[1],278 from: normalizeAddress(hData[2]),279 to: normalizeAddress(hData[3]),280 amount: BigInt(hData[4]),281 };282 }283 });284 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;285 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);286 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);287 isSuccess = isSuccess && amount === transfer.amount;288 return isSuccess;289 }290291 static bigIntToDecimals(number: bigint, decimals = 18) {292 const numberStr = number.toString();293 const dotPos = numberStr.length - decimals;294295 if (dotPos <= 0) {296 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;297 } else {298 const intPart = numberStr.substring(0, dotPos);299 const fractPart = numberStr.substring(dotPos);300 return intPart + '.' + fractPart;301 }302 }303}304305class UniqueEventHelper {306 private static extractIndex(index: any): [number, number] | string {307 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];308 return index.toJSON();309 }310311 private static extractSub(data: any, subTypes: any): {[key: string]: any} {312 let obj: any = {};313 let index = 0;314315 if (data.entries) {316 for(const [key, value] of data.entries()) {317 obj[key] = this.extractData(value, subTypes[index]);318 index++;319 }320 } else obj = data.toJSON();321322 return obj;323 }324325 private static extractData(data: any, type: any): any {326 if(!type) return data.toHuman();327 if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();328 if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();329 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);330 return data.toHuman();331 }332333 public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {334 const parsedEvents: IEvent[] = [];335336 events.forEach((record) => {337 const {event, phase} = record;338 const types = event.typeDef;339340 const eventData: IEvent = {341 section: event.section.toString(),342 method: event.method.toString(),343 index: this.extractIndex(event.index),344 data: [],345 phase: phase.toJSON(),346 };347348 event.data.forEach((val: any, index: number) => {349 eventData.data.push(this.extractData(val, types[index]));350 });351352 parsedEvents.push(eventData);353 });354355 return parsedEvents;356 }357}358359export class ChainHelperBase {360 helperBase: any;361362 transactionStatus = UniqueUtil.transactionStatus;363 chainLogType = UniqueUtil.chainLogType;364 util: typeof UniqueUtil;365 eventHelper: typeof UniqueEventHelper;366 logger: ILogger;367 api: ApiPromise | null;368 forcedNetwork: TNetworks | null;369 network: TNetworks | null;370 chainLog: IUniqueHelperLog[];371 children: ChainHelperBase[];372 address: AddressGroup;373 chain: ChainGroup;374375 constructor(logger?: ILogger, helperBase?: any) {376 this.helperBase = helperBase;377378 this.util = UniqueUtil;379 this.eventHelper = UniqueEventHelper;380 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();381 this.logger = logger;382 this.api = null;383 this.forcedNetwork = null;384 this.network = null;385 this.chainLog = [];386 this.children = [];387 this.address = new AddressGroup(this);388 this.chain = new ChainGroup(this);389 }390391 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {392 Object.setPrototypeOf(helperCls.prototype, this);393 const newHelper = new helperCls(this.logger, options);394395 newHelper.api = this.api;396 newHelper.network = this.network;397 newHelper.forceNetwork = this.forceNetwork;398399 this.children.push(newHelper);400401 return newHelper;402 }403404 getApi(): ApiPromise {405 if(this.api === null) throw Error('API not initialized');406 return this.api;407 }408409 async subscribeEvents(expectedEvents: {section: string, names: string[]}[]) {410 const collectedEvents: IEvent[] = [];411 const unsubscribe = await this.getApi().query.system.events((events: Vec<FrameSystemEventRecord>) => {412 const ievents = this.eventHelper.extractEvents(events);413 ievents.forEach((event) => {414 expectedEvents.forEach((e => {415 if (event.section === e.section && e.names.includes(event.method)) {416 collectedEvents.push(event);417 }418 }))419 });420 });421 return {unsubscribe: unsubscribe as any, collectedEvents};422}423424 clearChainLog(): void {425 this.chainLog = [];426 }427428 forceNetwork(value: TNetworks): void {429 this.forcedNetwork = value;430 }431432 async connect(wsEndpoint: string, listeners?: IApiListeners) {433 if (this.api !== null) throw Error('Already connected');434 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);435 this.api = api;436 this.network = network;437 }438439 async disconnect() {440 for (const child of this.children) {441 child.clearApi();442 }443444 if (this.api === null) return;445 await this.api.disconnect();446 this.clearApi();447 }448449 clearApi() {450 this.api = null;451 this.network = null;452 }453454 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {455 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;456 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];457458 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;459460 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;461 return 'opal';462 }463464 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {465 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});466 await api.isReady;467468 const network = await this.detectNetwork(api);469470 await api.disconnect();471472 return network;473 }474475 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{476 api: ApiPromise;477 network: TNetworks;478 }> {479 if(typeof network === 'undefined' || network === null) network = 'opal';480 const supportedRPC = {481 opal: {482 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,483 },484 quartz: {485 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,486 },487 unique: {488 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,489 },490 rococo: {},491 westend: {},492 moonbeam: {},493 moonriver: {},494 acala: {},495 karura: {},496 westmint: {},497 };498 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);499 const rpc = supportedRPC[network];500501 502 503504 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});505506 await api.isReadyOrError;507508 if (typeof listeners === 'undefined') listeners = {};509 for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {510 if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;511 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);512 }513514 return {api, network};515 }516517 getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {518 const {events, status} = data;519 if (status.isReady) {520 return this.transactionStatus.NOT_READY;521 }522 if (status.isBroadcast) {523 return this.transactionStatus.NOT_READY;524 }525 if (status.isInBlock || status.isFinalized) {526 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');527 if (errors.length > 0) {528 return this.transactionStatus.FAIL;529 }530 if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {531 return this.transactionStatus.SUCCESS;532 }533 }534535 return this.transactionStatus.FAIL;536 }537538 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {539 const sign = (callback: any) => {540 if(options !== null) return transaction.signAndSend(sender, options, callback);541 return transaction.signAndSend(sender, callback);542 };543 544 return new Promise(async (resolve, reject) => {545 try {546 const unsub = await sign((result: any) => {547 const status = this.getTransactionStatus(result);548549 if (status === this.transactionStatus.SUCCESS) {550 this.logger.log(`${label} successful`);551 unsub();552 resolve({result, status});553 } else if (status === this.transactionStatus.FAIL) {554 let moduleError = null;555556 if (result.hasOwnProperty('dispatchError')) {557 const dispatchError = result['dispatchError'];558559 if (dispatchError) {560 if (dispatchError.isModule) {561 const modErr = dispatchError.asModule;562 const errorMeta = dispatchError.registry.findMetaError(modErr);563564 moduleError = `${errorMeta.section}.${errorMeta.name}`;565 } else {566 moduleError = dispatchError.toHuman();567 }568 } else {569 this.logger.log(result, this.logger.level.ERROR);570 }571 }572573 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);574 unsub();575 reject({status, moduleError, result});576 }577 });578 } catch (e) {579 this.logger.log(e, this.logger.level.ERROR);580 reject(e);581 }582 });583 }584585 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {586 const api = this.getApi();587 const signingInfo = await api.derive.tx.signingInfo(signer.address);588589 590 591 tx.sign(signer, {592 blockHash: api.genesisHash,593 genesisHash: api.genesisHash,594 runtimeVersion: api.runtimeVersion,595 nonce: signingInfo.nonce,596 });597598 if (len === null) {599 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;600 } else {601 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;602 }603 }604605 constructApiCall(apiCall: string, params: any[]) {606 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);607 let call = this.getApi() as any;608 for(const part of apiCall.slice(4).split('.')) {609 call = call[part];610 }611 return call(...params);612 }613614 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null) {615 if(this.api === null) throw Error('API not initialized');616 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);617618 const startTime = (new Date()).getTime();619 let result: ITransactionResult;620 let events: IEvent[] = [];621 try {622 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;623 events = this.eventHelper.extractEvents(result.result.events);624 }625 catch(e) {626 if(!(e as object).hasOwnProperty('status')) throw e;627 result = e as ITransactionResult;628 }629630 const endTime = (new Date()).getTime();631632 const log = {633 executedAt: endTime,634 executionTime: endTime - startTime,635 type: this.chainLogType.EXTRINSIC,636 status: result.status,637 call: extrinsic,638 signer: this.getSignerAddress(sender),639 params,640 } as IUniqueHelperLog;641642 if(result.status !== this.transactionStatus.SUCCESS) {643 if (result.moduleError) log.moduleError = result.moduleError;644 else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;645 }646 if(events.length > 0) log.events = events;647648 this.chainLog.push(log);649650 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {651 if (result.moduleError) throw Error(`${result.moduleError}`);652 else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));653 }654 return result;655 }656657 async callRpc(rpc: string, params?: any[]) {658 if(typeof params === 'undefined') params = [];659 if(this.api === null) throw Error('API not initialized');660 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);661662 const startTime = (new Date()).getTime();663 let result;664 let error = null;665 const log = {666 type: this.chainLogType.RPC,667 call: rpc,668 params,669 } as IUniqueHelperLog;670671 try {672 result = await this.constructApiCall(rpc, params);673 }674 catch(e) {675 error = e;676 }677678 const endTime = (new Date()).getTime();679680 log.executedAt = endTime;681 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';682 log.executionTime = endTime - startTime;683684 this.chainLog.push(log);685686 if(error !== null) throw error;687688 return result;689 }690691 getSignerAddress(signer: IKeyringPair | string): string {692 if(typeof signer === 'string') return signer;693 return signer.address;694 }695696 fetchAllPalletNames(): string[] {697 if(this.api === null) throw Error('API not initialized');698 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());699 }700701 fetchMissingPalletNames(requiredPallets: string[]): string[] {702 const palletNames = this.fetchAllPalletNames();703 return requiredPallets.filter(p => !palletNames.includes(p));704 }705}706707708class HelperGroup<T extends ChainHelperBase> {709 helper: T;710711 constructor(uniqueHelper: T) {712 this.helper = uniqueHelper;713 }714}715716717class CollectionGroup extends HelperGroup<UniqueHelper> {718 719720721722723724725726727 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {728 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();729 }730731 732733734735736 async getTotalCount(): Promise<number> {737 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();738 }739740 741742743744745746747748749 async getData(collectionId: number): Promise<{750 id: number;751 name: string;752 description: string;753 tokensCount: number;754 admins: CrossAccountId[];755 normalizedOwner: TSubstrateAccount;756 raw: any757 } | null> {758 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);759 const humanCollection = collection.toHuman(), collectionData = {760 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],761 raw: humanCollection,762 } as any, jsonCollection = collection.toJSON();763 if (humanCollection === null) return null;764 collectionData.raw.limits = jsonCollection.limits;765 collectionData.raw.permissions = jsonCollection.permissions;766 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);767 for (const key of ['name', 'description']) {768 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);769 }770771 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))772 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)773 : 0;774 collectionData.admins = await this.getAdmins(collectionId);775776 return collectionData;777 }778779 780781782783784785786787 async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {788 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();789790 return normalize791 ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())792 : admins;793 }794795 796797798799800801802 async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {803 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();804 return normalize805 ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())806 : allowListed;807 }808809 810811812813814815816 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {817 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();818 }819820 821822823824825826827828 async burn(signer: TSigner, collectionId: number): Promise<boolean> {829 const result = await this.helper.executeExtrinsic(830 signer,831 'api.tx.unique.destroyCollection', [collectionId],832 true,833 );834835 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');836 }837838 839840841842843844845846847 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {848 const result = await this.helper.executeExtrinsic(849 signer,850 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],851 true,852 );853854 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');855 }856857 858859860861862863864865 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {866 const result = await this.helper.executeExtrinsic(867 signer,868 'api.tx.unique.confirmSponsorship', [collectionId],869 true,870 );871872 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');873 }874875 876877878879880881882883 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {884 const result = await this.helper.executeExtrinsic(885 signer,886 'api.tx.unique.removeCollectionSponsor', [collectionId],887 true,888 );889890 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');891 }892893 894895896897898899900901902903904905906907908909910 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {911 const result = await this.helper.executeExtrinsic(912 signer,913 'api.tx.unique.setCollectionLimits', [collectionId, limits],914 true,915 );916917 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');918 }919920 921922923924925926927928929 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {930 const result = await this.helper.executeExtrinsic(931 signer,932 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],933 true,934 );935936 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnedChanged');937 }938939 940941942943944945946947948 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {949 const result = await this.helper.executeExtrinsic(950 signer,951 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],952 true,953 );954955 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');956 }957958 959960961962963964965966967 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {968 const result = await this.helper.executeExtrinsic(969 signer,970 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],971 true,972 );973974 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');975 }976977 978979980981982983984985 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {986 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();987 }988989 990991992993994995996 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {997 const result = await this.helper.executeExtrinsic(998 signer,999 'api.tx.unique.addToAllowList', [collectionId, addressObj],1000 true,1001 );10021003 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');1004 }10051006 10071008100910101011101210131014 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1015 const result = await this.helper.executeExtrinsic(1016 signer,1017 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1018 true,1019 );10201021 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');1022 }10231024 102510261027102810291030103110321033 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1034 const result = await this.helper.executeExtrinsic(1035 signer,1036 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1037 true,1038 );10391040 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');1041 }10421043 104410451046104710481049105010511052 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1053 return await this.setPermissions(signer, collectionId, {nesting: permissions});1054 }10551056 10571058105910601061106210631064 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1065 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1066 }10671068 106910701071107210731074107510761077 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1078 const result = await this.helper.executeExtrinsic(1079 signer,1080 'api.tx.unique.setCollectionProperties', [collectionId, properties],1081 true,1082 );10831084 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1085 }10861087 10881089109010911092109310941095 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1096 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1097 }10981099 async getCollectionOptions(collectionId: number) {1100 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1101 }11021103 110411051106110711081109111011111112 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1113 const result = await this.helper.executeExtrinsic(1114 signer,1115 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1116 true,1117 );11181119 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1120 }11211122 11231124112511261127112811291130113111321133 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1134 const result = await this.helper.executeExtrinsic(1135 signer,1136 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1137 true, 1138 );11391140 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1141 }11421143 1144114511461147114811491150115111521153115411551156 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1157 const result = await this.helper.executeExtrinsic(1158 signer,1159 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1160 true, 1161 );1162 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1163 }11641165 11661167116811691170117111721173117411751176 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1177 const burnResult = await this.helper.executeExtrinsic(1178 signer,1179 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1180 true, 1181 );1182 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1183 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1184 return burnedTokens.success;1185 }11861187 11881189119011911192119311941195119611971198 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1199 const burnResult = await this.helper.executeExtrinsic(1200 signer,1201 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1202 true, 1203 );1204 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1205 return burnedTokens.success && burnedTokens.tokens.length > 0;1206 }12071208 1209121012111212121312141215121612171218 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1219 const approveResult = await this.helper.executeExtrinsic(1220 signer,1221 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1222 true, 1223 );12241225 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1226 }12271228 1229123012311232123312341235123612371238 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1239 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1240 }12411242 1243124412451246124712481249 async getLastTokenId(collectionId: number): Promise<number> {1250 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1251 }12521253 12541255125612571258125912601261 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1262 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1263 }1264}12651266class NFTnRFT extends CollectionGroup {1267 12681269127012711272127312741275 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1276 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1277 }12781279 1280128112821283128412851286128712881289 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1290 properties: IProperty[];1291 owner: CrossAccountId;1292 normalizedOwner: CrossAccountId;1293 }| null> {1294 let tokenData;1295 if(typeof blockHashAt === 'undefined') {1296 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1297 }1298 else {1299 if(propertyKeys.length == 0) {1300 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1301 if(!collection) return null;1302 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1303 }1304 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1305 }1306 tokenData = tokenData.toHuman();1307 if (tokenData === null || tokenData.owner === null) return null;1308 const owner = {} as any;1309 for (const key of Object.keys(tokenData.owner)) {1310 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1311 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1312 : tokenData.owner[key];1313 }1314 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1315 return tokenData;1316 }13171318 13191320132113221323132413251326132713281329 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1330 const result = await this.helper.executeExtrinsic(1331 signer,1332 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1333 true,1334 );13351336 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1337 }13381339 13401341134213431344134513461347 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1348 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1349 }13501351 1352135313541355135613571358135913601361 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1362 const result = await this.helper.executeExtrinsic(1363 signer,1364 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1365 true,1366 );13671368 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1369 }13701371 137213731374137513761377137813791380 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1381 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1382 }13831384 138513861387138813891390139113921393 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1394 const result = await this.helper.executeExtrinsic(1395 signer,1396 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1397 true,1398 );13991400 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1401 }14021403 140414051406140714081409141014111412 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1413 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1414 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1415 for (const key of ['name', 'description', 'tokenPrefix']) {1416 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);1417 }1418 const creationResult = await this.helper.executeExtrinsic(1419 signer,1420 'api.tx.unique.createCollectionEx', [collectionOptions],1421 true, 1422 );1423 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1424 }14251426 getCollectionObject(_collectionId: number): any {1427 return null;1428 }14291430 getTokenObject(_collectionId: number, _tokenId: number): any {1431 return null;1432 }14331434 1435143614371438143914401441 async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1442 return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1443 }14441445 144614471448144914501451 async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1452 const result = await this.helper.executeExtrinsic(1453 signer,1454 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1455 true,1456 );1457 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1458 }1459}146014611462class NFTGroup extends NFTnRFT {1463 146414651466146714681469 getCollectionObject(collectionId: number): UniqueNFTCollection {1470 return new UniqueNFTCollection(collectionId, this.helper);1471 }14721473 1474147514761477147814791480 getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1481 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1482 }14831484 14851486148714881489149014911492 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1493 let owner;1494 if (typeof blockHashAt === 'undefined') {1495 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1496 } else {1497 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1498 }1499 return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1500 }15011502 1503150415051506150715081509 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1510 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1511 }15121513 1514151515161517151815191520152115221523 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1524 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1525 }15261527 152815291530153115321533153415351536153715381539 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1540 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1541 }15421543 15441545154615471548154915501551 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1552 let owner;1553 if (typeof blockHashAt === 'undefined') {1554 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1555 } else {1556 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1557 }15581559 if (owner === null) return null;15601561 return owner.toHuman();1562 }15631564 15651566156715681569157015711572 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1573 let children;1574 if(typeof blockHashAt === 'undefined') {1575 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1576 } else {1577 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1578 }15791580 return children.toJSON().map((x: any) => {1581 return {collectionId: x.collection, tokenId: x.token};1582 });1583 }15841585 15861587158815891590159115921593 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1594 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1595 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1596 if(!result) {1597 throw Error('Unable to nest token!');1598 }1599 return result;1600 }16011602 160316041605160616071608160916101611 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1612 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1613 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1614 if(!result) {1615 throw Error('Unable to unnest token!');1616 }1617 return result;1618 }16191620 162116221623162416251626162716281629163016311632 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1633 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1634 }16351636 163716381639164016411642 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1643 const creationResult = await this.helper.executeExtrinsic(1644 signer,1645 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1646 nft: {1647 properties: data.properties,1648 },1649 }],1650 true,1651 );1652 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1653 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1654 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1655 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1656 }16571658 165916601661166216631664166516661667166816691670167116721673 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1674 const creationResult = await this.helper.executeExtrinsic(1675 signer,1676 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1677 true,1678 );1679 const collection = this.getCollectionObject(collectionId);1680 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1681 }16821683 168416851686168716881689169016911692169316941695169616971698169917001701 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1702 const rawTokens = [];1703 for (const token of tokens) {1704 const raw = {NFT: {properties: token.properties}};1705 rawTokens.push(raw);1706 }1707 const creationResult = await this.helper.executeExtrinsic(1708 signer,1709 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1710 true,1711 );1712 const collection = this.getCollectionObject(collectionId);1713 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1714 }17151716 1717171817191720172117221723172417251726 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1727 return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1728 }1729}173017311732class RFTGroup extends NFTnRFT {1733 173417351736173717381739 getCollectionObject(collectionId: number): UniqueRFTCollection {1740 return new UniqueRFTCollection(collectionId, this.helper);1741 }17421743 1744174517461747174817491750 getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1751 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1752 }17531754 1755175617571758175917601761 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1762 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1763 }17641765 17661767176817691770177117721773 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1774 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1775 }17761777 1778177917801781178217831784178517861787 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1788 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1789 }17901791 17921793179417951796179717981799180018011802 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1803 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1804 }18051806 180718081809181018111812181318141815181618171818 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1819 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1820 }18211822 1823182418251826182718281829 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1830 const creationResult = await this.helper.executeExtrinsic(1831 signer,1832 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1833 refungible: {1834 pieces: data.pieces,1835 properties: data.properties,1836 },1837 }],1838 true,1839 );1840 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1841 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1842 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1843 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1844 }18451846 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1847 throw Error('Not implemented');1848 const creationResult = await this.helper.executeExtrinsic(1849 signer,1850 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1851 true, 1852 );1853 const collection = this.getCollectionObject(collectionId);1854 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1855 }18561857 185818591860186118621863186418651866 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1867 const rawTokens = [];1868 for (const token of tokens) {1869 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1870 rawTokens.push(raw);1871 }1872 const creationResult = await this.helper.executeExtrinsic(1873 signer,1874 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1875 true,1876 );1877 const collection = this.getCollectionObject(collectionId);1878 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1879 }18801881 188218831884188518861887188818891890 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1891 return await super.burnToken(signer, collectionId, tokenId, amount);1892 }18931894 1895189618971898189919001901190219031904 async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1905 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1906 }19071908 19091910191119121913191419151916191719181919 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1920 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1921 }19221923 1924192519261927192819291930 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1931 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1932 }19331934 193519361937193819391940194119421943 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1944 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1945 const repartitionResult = await this.helper.executeExtrinsic(1946 signer,1947 'api.tx.unique.repartition', [collectionId, tokenId, amount],1948 true,1949 );1950 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1951 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1952 }1953}195419551956class FTGroup extends CollectionGroup {1957 195819591960196119621963 getCollectionObject(collectionId: number): UniqueFTCollection {1964 return new UniqueFTCollection(collectionId, this.helper);1965 }19661967 1968196919701971197219731974197519761977197819791980 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1981 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1982 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1983 collectionOptions.mode = {fungible: decimalPoints};1984 for (const key of ['name', 'description', 'tokenPrefix']) {1985 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);1986 }1987 const creationResult = await this.helper.executeExtrinsic(1988 signer,1989 'api.tx.unique.createCollectionEx', [collectionOptions],1990 true,1991 );1992 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1993 }19941995 199619971998199920002001200220032004 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {2005 const creationResult = await this.helper.executeExtrinsic(2006 signer,2007 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {2008 fungible: {2009 value: amount,2010 },2011 }],2012 true, 2013 );2014 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2015 }20162017 20182019202020212022202320242025 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {2026 const rawTokens = [];2027 for (const token of tokens) {2028 const raw = {Fungible: {Value: token.value}};2029 rawTokens.push(raw);2030 }2031 const creationResult = await this.helper.executeExtrinsic(2032 signer,2033 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2034 true,2035 );2036 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2037 }20382039 204020412042204320442045 async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2046 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2047 }20482049 2050205120522053205420552056 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2057 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2058 }20592060 206120622063206420652066206720682069 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2070 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2071 }20722073 2074207520762077207820792080208120822083 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2084 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2085 }20862087 20882089209020912092209320942095 async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2096 return await super.burnToken(signer, collectionId, 0, amount);2097 }20982099 210021012102210321042105210621072108 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2109 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2110 }21112112 21132114211521162117 async getTotalPieces(collectionId: number): Promise<bigint> {2118 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2119 }21202121 2122212321242125212621272128212921302131 approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2132 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2133 }21342135 2136213721382139214021412142 getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2143 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2144 }2145}214621472148class ChainGroup extends HelperGroup<ChainHelperBase> {2149 21502151215221532154 getChainProperties(): IChainProperties {2155 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2156 return {2157 ss58Format: properties.ss58Format.toJSON(),2158 tokenDecimals: properties.tokenDecimals.toJSON(),2159 tokenSymbol: properties.tokenSymbol.toJSON(),2160 };2161 }21622163 21642165216621672168 async getLatestBlockNumber(): Promise<number> {2169 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2170 }21712172 217321742175217621772178 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2179 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2180 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2181 return blockHash;2182 }21832184 2185 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2186 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2187 if (!blockHash) return null;2188 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2189 }21902191 219221932194219521962197 async getNonce(address: TSubstrateAccount): Promise<number> {2198 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2199 }2200}22012202class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2203 220422052206220722082209 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2210 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2211 }22122213 22142215221622172218221922202221 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2222 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22232224 let transfer = {from: null, to: null, amount: 0n} as any;2225 result.result.events.forEach(({event: {data, method, section}}) => {2226 if ((section === 'balances') && (method === 'Transfer')) {2227 transfer = {2228 from: this.helper.address.normalizeSubstrate(data[0]),2229 to: this.helper.address.normalizeSubstrate(data[1]),2230 amount: BigInt(data[2]),2231 };2232 }2233 });2234 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2235 && this.helper.address.normalizeSubstrate(address) === transfer.to2236 && BigInt(amount) === transfer.amount;2237 return isSuccess;2238 }22392240 22412242224322442245 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2246 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2247 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2248 }2249}22502251class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2252 225322542255225622572258 async getEthereum(address: TEthereumAccount): Promise<bigint> {2259 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2260 }22612262 22632264226522662267226822692270 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2271 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22722273 let transfer = {from: null, to: null, amount: 0n} as any;2274 result.result.events.forEach(({event: {data, method, section}}) => {2275 if ((section === 'balances') && (method === 'Transfer')) {2276 transfer = {2277 from: data[0].toString(),2278 to: data[1].toString(),2279 amount: BigInt(data[2]),2280 };2281 }2282 });2283 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2284 && address === transfer.to2285 && BigInt(amount) === transfer.amount;2286 return isSuccess;2287 }2288}22892290class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2291 subBalanceGroup: SubstrateBalanceGroup<T>;2292 ethBalanceGroup: EthereumBalanceGroup<T>;22932294 constructor(helper: T) {2295 super(helper);2296 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2297 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2298 }22992300 getCollectionCreationPrice(): bigint {2301 return 2n * this.getOneTokenNominal();2302 }2303 23042305230623072308 getOneTokenNominal(): bigint {2309 const chainProperties = this.helper.chain.getChainProperties();2310 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2311 }23122313 231423152316231723182319 getSubstrate(address: TSubstrateAccount): Promise<bigint> {2320 return this.subBalanceGroup.getSubstrate(address);2321 }23222323 23242325232623272328 getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2329 return this.subBalanceGroup.getSubstrateFull(address);2330 }23312332 233323342335233623372338 getEthereum(address: TEthereumAccount): Promise<bigint> {2339 return this.ethBalanceGroup.getEthereum(address);2340 }23412342 23432344234523462347234823492350 transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2351 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2352 }23532354 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2355 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);23562357 let transfer = {from: null, to: null, amount: 0n} as any;2358 result.result.events.forEach(({event: {data, method, section}}) => {2359 if ((section === 'balances') && (method === 'Transfer')) {2360 transfer = {2361 from: this.helper.address.normalizeSubstrate(data[0]),2362 to: this.helper.address.normalizeSubstrate(data[1]),2363 amount: BigInt(data[2]),2364 };2365 }2366 });2367 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2368 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2369 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2370 return isSuccess;2371 }2372}23732374class AddressGroup extends HelperGroup<ChainHelperBase> {2375 2376237723782379238023812382 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2383 return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2384 }23852386 238723882389239023912392 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2393 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2394 }23952396 2397239823992400240124022403 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2404 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2405 }24062407 240824092410241124122413 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2414 return CrossAccountId.translateSubToEth(subAddress);2415 }24162417 241824192420242124222423 encodeSubstrateAddress (key: Uint8Array | string | bigint, ss58Format = 42): string {2424 const u8a :Uint8Array = typeof key === 'string'2425 ? hexToU8a(key)2426 : typeof key === 'bigint'2427 ? hexToU8a(key.toString(16))2428 : key;2429 2430 if (ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2431 throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2432 }2433 2434 const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2435 if (!allowedDecodedLengths.includes(u8a.length)) {2436 throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2437 }2438 2439 const u8aPrefix = ss58Format < 642440 ? new Uint8Array([ss58Format])2441 : new Uint8Array([2442 ((ss58Format & 0xfc) >> 2) | 0x40,2443 (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2444 ]);24452446 const input = u8aConcat(u8aPrefix, u8a);2447 2448 return base58Encode(u8aConcat(2449 input,2450 blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2451 ));2452 }24532454 24552456245724582459 restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2460 if (this.helper.api === null) {2461 throw 'Not connected';2462 }2463 const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2464 if (res === undefined || res === null) {2465 throw 'Restore address error';2466 }2467 return res.toString();2468 }24692470 24712472247324742475 convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2476 if (ethCrossAccount.sub === '0') {2477 return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2478 }2479 2480 const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2481 return {Substrate: ss58};2482 }24832484 paraSiblingSovereignAccount(paraid: number) {2485 2486 2487 const siblingPrefix = '0x7369626c';24882489 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2490 const suffix = '000000000000000000000000000000000000000000000000';24912492 return siblingPrefix + encodedParaId + suffix;2493 }2494}24952496class StakingGroup extends HelperGroup<UniqueHelper> {2497 2498249925002501250225032504 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2505 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2506 const _stakeResult = await this.helper.executeExtrinsic(2507 signer, 'api.tx.appPromotion.stake',2508 [amountToStake], true,2509 );2510 2511 return true;2512 }25132514 2515251625172518251925202521 async unstake(signer: TSigner, label?: string): Promise<number> {2522 if(typeof label === 'undefined') label = `${signer.address}`;2523 const _unstakeResult = await this.helper.executeExtrinsic(2524 signer, 'api.tx.appPromotion.unstake',2525 [], true,2526 );2527 2528 return 1;2529 }25302531 25322533253425352536 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2537 if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2538 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2539 }25402541 25422543254425452546 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2547 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2548 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2549 return {2550 block: block.toBigInt(),2551 amount: amount.toBigInt(),2552 };2553 });2554 }25552556 25572558255925602561 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2562 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2563 }25642565 25662567256825692570 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2571 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2572 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2573 return {2574 block: block.toBigInt(),2575 amount: amount.toBigInt(),2576 };2577 });2578 return result;2579 }2580}25812582class SchedulerGroup extends HelperGroup<UniqueHelper> {2583 constructor(helper: UniqueHelper) {2584 super(helper);2585 }25862587 cancelScheduled(signer: TSigner, scheduledId: string) {2588 return this.helper.executeExtrinsic(2589 signer,2590 'api.tx.scheduler.cancelNamed',2591 [scheduledId],2592 true,2593 );2594 }25952596 changePriority(signer: TSigner, scheduledId: string, priority: number) {2597 return this.helper.executeExtrinsic(2598 signer,2599 'api.tx.scheduler.changeNamedPriority',2600 [scheduledId, priority],2601 true,2602 );2603 }26042605 scheduleAt<T extends UniqueHelper>(2606 executionBlockNumber: number,2607 options: ISchedulerOptions = {},2608 ) {2609 return this.schedule<T>('schedule', executionBlockNumber, options);2610 }26112612 scheduleAfter<T extends UniqueHelper>(2613 blocksBeforeExecution: number,2614 options: ISchedulerOptions = {},2615 ) {2616 return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);2617 }26182619 schedule<T extends UniqueHelper>(2620 scheduleFn: 'schedule' | 'scheduleAfter',2621 blocksNum: number,2622 options: ISchedulerOptions = {},2623 ) {2624 2625 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2626 return this.helper.clone(ScheduledHelperType, {2627 scheduleFn,2628 blocksNum,2629 options,2630 }) as T;2631 }2632}26332634class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2635 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2636 await this.helper.executeExtrinsic(2637 signer,2638 'api.tx.foreignAssets.registerForeignAsset',2639 [ownerAddress, location, metadata],2640 true,2641 );2642 }26432644 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2645 await this.helper.executeExtrinsic(2646 signer,2647 'api.tx.foreignAssets.updateForeignAsset',2648 [foreignAssetId, location, metadata],2649 true,2650 );2651 }2652}26532654class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2655 palletName: string;26562657 constructor(helper: T, palletName: string) {2658 super(helper);26592660 this.palletName = palletName;2661 }26622663 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2664 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2665 }2666}26672668class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2669 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2670 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2671 }26722673 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2674 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2675 }26762677 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2678 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2679 }2680}26812682class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2683 async accounts(address: string, currencyId: any) {2684 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2685 return BigInt(free);2686 }2687}26882689class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2690 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2691 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2692 }26932694 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2695 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2696 }26972698 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2699 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2700 }27012702 async account(assetId: string | number, address: string) {2703 const accountAsset = (2704 await this.helper.callRpc('api.query.assets.account', [assetId, address])2705 ).toJSON()! as any;27062707 if (accountAsset !== null) {2708 return BigInt(accountAsset['balance']);2709 } else {2710 return null;2711 }2712 }2713}27142715class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2716 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2717 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2718 }2719}27202721class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2722 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2723 const apiPrefix = 'api.tx.assetManager.';27242725 const registerTx = this.helper.constructApiCall(2726 apiPrefix + 'registerForeignAsset',2727 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2728 );27292730 const setUnitsTx = this.helper.constructApiCall(2731 apiPrefix + 'setAssetUnitsPerSecond',2732 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2733 );27342735 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2736 const encodedProposal = batchCall?.method.toHex() || '';2737 return encodedProposal;2738 }27392740 async assetTypeId(location: any) {2741 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2742 }2743}27442745class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2746 async notePreimage(signer: TSigner, encodedProposal: string) {2747 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2748 }27492750 externalProposeMajority(proposalHash: string) {2751 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2752 }27532754 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2755 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2756 }27572758 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2759 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2760 }2761}27622763class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2764 collective: string;27652766 constructor(helper: MoonbeamHelper, collective: string) {2767 super(helper);27682769 this.collective = collective;2770 }27712772 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2773 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2774 }27752776 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2777 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2778 }27792780 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2781 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2782 }27832784 async proposalCount() {2785 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2786 }2787}27882789export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2790export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;27912792export class UniqueHelper extends ChainHelperBase {2793 balance: BalanceGroup<UniqueHelper>;2794 collection: CollectionGroup;2795 nft: NFTGroup;2796 rft: RFTGroup;2797 ft: FTGroup;2798 staking: StakingGroup;2799 scheduler: SchedulerGroup;2800 foreignAssets: ForeignAssetsGroup;2801 xcm: XcmGroup<UniqueHelper>;2802 xTokens: XTokensGroup<UniqueHelper>;2803 tokens: TokensGroup<UniqueHelper>;28042805 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2806 super(logger, options.helperBase ?? UniqueHelper);28072808 this.balance = new BalanceGroup(this);2809 this.collection = new CollectionGroup(this);2810 this.nft = new NFTGroup(this);2811 this.rft = new RFTGroup(this);2812 this.ft = new FTGroup(this);2813 this.staking = new StakingGroup(this);2814 this.scheduler = new SchedulerGroup(this);2815 this.foreignAssets = new ForeignAssetsGroup(this);2816 this.xcm = new XcmGroup(this, 'polkadotXcm');2817 this.xTokens = new XTokensGroup(this);2818 this.tokens = new TokensGroup(this);2819 }28202821 getSudo<T extends UniqueHelper>() {2822 2823 const SudoHelperType = SudoHelper(this.helperBase);2824 return this.clone(SudoHelperType) as T;2825 }2826}28272828export class XcmChainHelper extends ChainHelperBase {2829 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2830 const wsProvider = new WsProvider(wsEndpoint);2831 this.api = new ApiPromise({2832 provider: wsProvider,2833 });2834 await this.api.isReadyOrError;2835 this.network = await UniqueHelper.detectNetwork(this.api);2836 }2837}28382839export class RelayHelper extends XcmChainHelper {2840 xcm: XcmGroup<RelayHelper>;28412842 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2843 super(logger, options.helperBase ?? RelayHelper);28442845 this.xcm = new XcmGroup(this, 'xcmPallet');2846 }2847}28482849export class WestmintHelper extends XcmChainHelper {2850 balance: SubstrateBalanceGroup<WestmintHelper>;2851 xcm: XcmGroup<WestmintHelper>;2852 assets: AssetsGroup<WestmintHelper>;2853 xTokens: XTokensGroup<WestmintHelper>;28542855 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2856 super(logger, options.helperBase ?? WestmintHelper);28572858 this.balance = new SubstrateBalanceGroup(this);2859 this.xcm = new XcmGroup(this, 'polkadotXcm');2860 this.assets = new AssetsGroup(this);2861 this.xTokens = new XTokensGroup(this);2862 }2863}28642865export class MoonbeamHelper extends XcmChainHelper {2866 balance: EthereumBalanceGroup<MoonbeamHelper>;2867 assetManager: MoonbeamAssetManagerGroup;2868 assets: AssetsGroup<MoonbeamHelper>;2869 xTokens: XTokensGroup<MoonbeamHelper>;2870 democracy: MoonbeamDemocracyGroup;2871 collective: {2872 council: MoonbeamCollectiveGroup,2873 techCommittee: MoonbeamCollectiveGroup,2874 };28752876 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2877 super(logger, options.helperBase ?? MoonbeamHelper);28782879 this.balance = new EthereumBalanceGroup(this);2880 this.assetManager = new MoonbeamAssetManagerGroup(this);2881 this.assets = new AssetsGroup(this);2882 this.xTokens = new XTokensGroup(this);2883 this.democracy = new MoonbeamDemocracyGroup(this);2884 this.collective = {2885 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2886 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2887 };2888 }2889}28902891export class AcalaHelper extends XcmChainHelper {2892 balance: SubstrateBalanceGroup<AcalaHelper>;2893 assetRegistry: AcalaAssetRegistryGroup;2894 xTokens: XTokensGroup<AcalaHelper>;2895 tokens: TokensGroup<AcalaHelper>;28962897 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2898 super(logger, options.helperBase ?? AcalaHelper);28992900 this.balance = new SubstrateBalanceGroup(this);2901 this.assetRegistry = new AcalaAssetRegistryGroup(this);2902 this.xTokens = new XTokensGroup(this);2903 this.tokens = new TokensGroup(this);2904 }29052906 getSudo<T extends AcalaHelper>() {2907 2908 const SudoHelperType = SudoHelper(this.helperBase);2909 return this.clone(SudoHelperType) as T;2910 }2911}291229132914function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2915 return class extends Base {2916 scheduleFn: 'schedule' | 'scheduleAfter';2917 blocksNum: number;2918 options: ISchedulerOptions;29192920 constructor(...args: any[]) {2921 const logger = args[0] as ILogger;2922 const options = args[1] as {2923 scheduleFn: 'schedule' | 'scheduleAfter',2924 blocksNum: number,2925 options: ISchedulerOptions2926 };29272928 super(logger);29292930 this.scheduleFn = options.scheduleFn;2931 this.blocksNum = options.blocksNum;2932 this.options = options.options;2933 }29342935 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {2936 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);2937 2938 const mandatorySchedArgs = [2939 this.blocksNum,2940 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,2941 this.options.priority ?? null,2942 scheduledTx,2943 ];2944 2945 let schedArgs;2946 let scheduleFn;29472948 if (this.options.scheduledId) {2949 schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];29502951 if (this.scheduleFn == 'schedule') {2952 scheduleFn = 'scheduleNamed';2953 } else if (this.scheduleFn == 'scheduleAfter') {2954 scheduleFn = 'scheduleNamedAfter';2955 }2956 } else {2957 schedArgs = mandatorySchedArgs;2958 scheduleFn = this.scheduleFn;2959 }29602961 const extrinsic = 'api.tx.scheduler.' + scheduleFn;29622963 return super.executeExtrinsic(2964 sender,2965 extrinsic,2966 schedArgs,2967 expectSuccess,2968 );2969 }2970 };2971}297229732974function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {2975 return class extends Base {2976 constructor(...args: any[]) {2977 super(...args);2978 }29792980 executeExtrinsic (2981 sender: IKeyringPair,2982 extrinsic: string,2983 params: any[],2984 expectSuccess?: boolean,2985 ): Promise<ITransactionResult> {2986 const call = this.constructApiCall(extrinsic, params);2987 return super.executeExtrinsic(2988 sender,2989 'api.tx.sudo.sudo',2990 [call],2991 expectSuccess,2992 );2993 }2994 };2995}29962997export class UniqueBaseCollection {2998 helper: UniqueHelper;2999 collectionId: number;30003001 constructor(collectionId: number, uniqueHelper: UniqueHelper) {3002 this.collectionId = collectionId;3003 this.helper = uniqueHelper;3004 }30053006 async getData() {3007 return await this.helper.collection.getData(this.collectionId);3008 }30093010 async getLastTokenId() {3011 return await this.helper.collection.getLastTokenId(this.collectionId);3012 }30133014 async doesTokenExist(tokenId: number) {3015 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);3016 }30173018 async getAdmins() {3019 return await this.helper.collection.getAdmins(this.collectionId);3020 }30213022 async getAllowList() {3023 return await this.helper.collection.getAllowList(this.collectionId);3024 }30253026 async getEffectiveLimits() {3027 return await this.helper.collection.getEffectiveLimits(this.collectionId);3028 }30293030 async getProperties(propertyKeys?: string[] | null) {3031 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3032 }30333034 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3035 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3036 }30373038 async getOptions() {3039 return await this.helper.collection.getCollectionOptions(this.collectionId);3040 }30413042 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {3043 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);3044 }30453046 async confirmSponsorship(signer: TSigner) {3047 return await this.helper.collection.confirmSponsorship(signer, this.collectionId);3048 }30493050 async removeSponsor(signer: TSigner) {3051 return await this.helper.collection.removeSponsor(signer, this.collectionId);3052 }30533054 async setLimits(signer: TSigner, limits: ICollectionLimits) {3055 return await this.helper.collection.setLimits(signer, this.collectionId, limits);3056 }30573058 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {3059 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);3060 }30613062 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3063 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);3064 }30653066 async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {3067 return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);3068 }30693070 async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {3071 return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);3072 }30733074 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3075 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);3076 }30773078 async setProperties(signer: TSigner, properties: IProperty[]) {3079 return await this.helper.collection.setProperties(signer, this.collectionId, properties);3080 }30813082 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3083 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);3084 }30853086 async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {3087 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);3088 }30893090 async enableNesting(signer: TSigner, permissions: INestingPermissions) {3091 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);3092 }30933094 async disableNesting(signer: TSigner) {3095 return await this.helper.collection.disableNesting(signer, this.collectionId);3096 }30973098 async burn(signer: TSigner) {3099 return await this.helper.collection.burn(signer, this.collectionId);3100 }31013102 scheduleAt<T extends UniqueHelper>(3103 executionBlockNumber: number,3104 options: ISchedulerOptions = {},3105 ) {3106 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3107 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3108 }31093110 scheduleAfter<T extends UniqueHelper>(3111 blocksBeforeExecution: number,3112 options: ISchedulerOptions = {},3113 ) {3114 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3115 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3116 }31173118 getSudo<T extends UniqueHelper>() {3119 return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());3120 }3121}312231233124export class UniqueNFTCollection extends UniqueBaseCollection {3125 getTokenObject(tokenId: number) {3126 return new UniqueNFToken(tokenId, this);3127 }31283129 async getTokensByAddress(addressObj: ICrossAccountId) {3130 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3131 }31323133 async getToken(tokenId: number, blockHashAt?: string) {3134 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3135 }31363137 async getTokenOwner(tokenId: number, blockHashAt?: string) {3138 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3139 }31403141 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3142 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3143 }31443145 async getTokenChildren(tokenId: number, blockHashAt?: string) {3146 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3147 }31483149 async getPropertyPermissions(propertyKeys: string[] | null = null) {3150 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3151 }31523153 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3154 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3155 }31563157 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3158 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3159 }31603161 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3162 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3163 }31643165 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3166 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3167 }31683169 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3170 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3171 }31723173 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3174 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3175 }31763177 async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3178 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3179 }31803181 async burnToken(signer: TSigner, tokenId: number) {3182 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3183 }31843185 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3186 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3187 }31883189 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3190 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3191 }31923193 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3194 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3195 }31963197 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3198 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3199 }32003201 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3202 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3203 }32043205 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3206 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3207 }32083209 scheduleAt<T extends UniqueHelper>(3210 executionBlockNumber: number,3211 options: ISchedulerOptions = {},3212 ) {3213 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3214 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3215 }32163217 scheduleAfter<T extends UniqueHelper>(3218 blocksBeforeExecution: number,3219 options: ISchedulerOptions = {},3220 ) {3221 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3222 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3223 }32243225 getSudo<T extends UniqueHelper>() {3226 return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3227 }3228}322932303231export class UniqueRFTCollection extends UniqueBaseCollection {3232 getTokenObject(tokenId: number) {3233 return new UniqueRFToken(tokenId, this);3234 }32353236 async getToken(tokenId: number, blockHashAt?: string) {3237 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3238 }32393240 async getTokensByAddress(addressObj: ICrossAccountId) {3241 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3242 }32433244 async getTop10TokenOwners(tokenId: number) {3245 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3246 }32473248 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3249 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3250 }32513252 async getTokenTotalPieces(tokenId: number) {3253 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3254 }32553256 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3257 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3258 }32593260 async getPropertyPermissions(propertyKeys: string[] | null = null) {3261 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3262 }32633264 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3265 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3266 }32673268 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3269 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3270 }32713272 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3273 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3274 }32753276 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3277 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3278 }32793280 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3281 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3282 }32833284 async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3285 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3286 }32873288 async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3289 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3290 }32913292 async burnToken(signer: TSigner, tokenId: number, amount=1n) {3293 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3294 }32953296 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n) {3297 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3298 }32993300 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3301 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3302 }33033304 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3305 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3306 }33073308 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3309 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3310 }33113312 scheduleAt<T extends UniqueHelper>(3313 executionBlockNumber: number,3314 options: ISchedulerOptions = {},3315 ) {3316 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3317 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3318 }33193320 scheduleAfter<T extends UniqueHelper>(3321 blocksBeforeExecution: number,3322 options: ISchedulerOptions = {},3323 ) {3324 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3325 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3326 }33273328 getSudo<T extends UniqueHelper>() {3329 return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3330 }3331}333233333334export class UniqueFTCollection extends UniqueBaseCollection {3335 async getBalance(addressObj: ICrossAccountId) {3336 return await this.helper.ft.getBalance(this.collectionId, addressObj);3337 }33383339 async getTotalPieces() {3340 return await this.helper.ft.getTotalPieces(this.collectionId);3341 }33423343 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3344 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3345 }33463347 async getTop10Owners() {3348 return await this.helper.ft.getTop10Owners(this.collectionId);3349 }33503351 async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3352 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3353 }33543355 async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3356 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3357 }33583359 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3360 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3361 }33623363 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3364 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3365 }33663367 async burnTokens(signer: TSigner, amount=1n) {3368 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3369 }33703371 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3372 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3373 }33743375 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3376 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3377 }33783379 scheduleAt<T extends UniqueHelper>(3380 executionBlockNumber: number,3381 options: ISchedulerOptions = {},3382 ) {3383 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3384 return new UniqueFTCollection(this.collectionId, scheduledHelper);3385 }33863387 scheduleAfter<T extends UniqueHelper>(3388 blocksBeforeExecution: number,3389 options: ISchedulerOptions = {},3390 ) {3391 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3392 return new UniqueFTCollection(this.collectionId, scheduledHelper);3393 }33943395 getSudo<T extends UniqueHelper>() {3396 return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3397 }3398}339934003401export class UniqueBaseToken {3402 collection: UniqueNFTCollection | UniqueRFTCollection;3403 collectionId: number;3404 tokenId: number;34053406 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3407 this.collection = collection;3408 this.collectionId = collection.collectionId;3409 this.tokenId = tokenId;3410 }34113412 async getNextSponsored(addressObj: ICrossAccountId) {3413 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3414 }34153416 async getProperties(propertyKeys?: string[] | null) {3417 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3418 }34193420 async setProperties(signer: TSigner, properties: IProperty[]) {3421 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3422 }34233424 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3425 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3426 }34273428 async doesExist() {3429 return await this.collection.doesTokenExist(this.tokenId);3430 }34313432 nestingAccount() {3433 return this.collection.helper.util.getTokenAccount(this);3434 }34353436 scheduleAt<T extends UniqueHelper>(3437 executionBlockNumber: number,3438 options: ISchedulerOptions = {},3439 ) {3440 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3441 return new UniqueBaseToken(this.tokenId, scheduledCollection);3442 }34433444 scheduleAfter<T extends UniqueHelper>(3445 blocksBeforeExecution: number,3446 options: ISchedulerOptions = {},3447 ) {3448 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3449 return new UniqueBaseToken(this.tokenId, scheduledCollection);3450 }34513452 getSudo<T extends UniqueHelper>() {3453 return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3454 }3455}345634573458export class UniqueNFToken extends UniqueBaseToken {3459 collection: UniqueNFTCollection;34603461 constructor(tokenId: number, collection: UniqueNFTCollection) {3462 super(tokenId, collection);3463 this.collection = collection;3464 }34653466 async getData(blockHashAt?: string) {3467 return await this.collection.getToken(this.tokenId, blockHashAt);3468 }34693470 async getOwner(blockHashAt?: string) {3471 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3472 }34733474 async getTopmostOwner(blockHashAt?: string) {3475 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3476 }34773478 async getChildren(blockHashAt?: string) {3479 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3480 }34813482 async nest(signer: TSigner, toTokenObj: IToken) {3483 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3484 }34853486 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3487 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3488 }34893490 async transfer(signer: TSigner, addressObj: ICrossAccountId) {3491 return await this.collection.transferToken(signer, this.tokenId, addressObj);3492 }34933494 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3495 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3496 }34973498 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3499 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3500 }35013502 async isApproved(toAddressObj: ICrossAccountId) {3503 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3504 }35053506 async burn(signer: TSigner) {3507 return await this.collection.burnToken(signer, this.tokenId);3508 }35093510 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3511 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3512 }35133514 scheduleAt<T extends UniqueHelper>(3515 executionBlockNumber: number,3516 options: ISchedulerOptions = {},3517 ) {3518 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3519 return new UniqueNFToken(this.tokenId, scheduledCollection);3520 }35213522 scheduleAfter<T extends UniqueHelper>(3523 blocksBeforeExecution: number,3524 options: ISchedulerOptions = {},3525 ) {3526 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3527 return new UniqueNFToken(this.tokenId, scheduledCollection);3528 }35293530 getSudo<T extends UniqueHelper>() {3531 return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3532 }3533}35343535export class UniqueRFToken extends UniqueBaseToken {3536 collection: UniqueRFTCollection;35373538 constructor(tokenId: number, collection: UniqueRFTCollection) {3539 super(tokenId, collection);3540 this.collection = collection;3541 }35423543 async getData(blockHashAt?: string) {3544 return await this.collection.getToken(this.tokenId, blockHashAt);3545 }35463547 async getTop10Owners() {3548 return await this.collection.getTop10TokenOwners(this.tokenId);3549 }35503551 async getBalance(addressObj: ICrossAccountId) {3552 return await this.collection.getTokenBalance(this.tokenId, addressObj);3553 }35543555 async getTotalPieces() {3556 return await this.collection.getTokenTotalPieces(this.tokenId);3557 }35583559 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3560 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3561 }35623563 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3564 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3565 }35663567 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3568 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3569 }35703571 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3572 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3573 }35743575 async repartition(signer: TSigner, amount: bigint) {3576 return await this.collection.repartitionToken(signer, this.tokenId, amount);3577 }35783579 async burn(signer: TSigner, amount=1n) {3580 return await this.collection.burnToken(signer, this.tokenId, amount);3581 }35823583 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3584 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3585 }35863587 scheduleAt<T extends UniqueHelper>(3588 executionBlockNumber: number,3589 options: ISchedulerOptions = {},3590 ) {3591 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3592 return new UniqueRFToken(this.tokenId, scheduledCollection);3593 }35943595 scheduleAfter<T extends UniqueHelper>(3596 blocksBeforeExecution: number,3597 options: ISchedulerOptions = {},3598 ) {3599 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3600 return new UniqueRFToken(this.tokenId, scheduledCollection);3601 }36023603 getSudo<T extends UniqueHelper>() {3604 return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3605 }3606}