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 {BN} from '@polkadot/util/bn';15import {16 IApiListeners,17 IBlock,18 IEvent,19 IChainProperties,20 ICollectionCreationOptions,21 ICollectionLimits,22 ICollectionPermissions,23 ICrossAccountId,24 ICrossAccountIdLower,25 ILogger,26 INestingPermissions,27 IProperty,28 IStakingInfo,29 ISchedulerOptions,30 ISubstrateBalance,31 IToken,32 ITokenPropertyPermission,33 ITransactionResult,34 IUniqueHelperLog,35 TApiAllowedListeners,36 TEthereumAccount,37 TSigner,38 TSubstrateAccount,39 TNetworks,40 IForeignAssetMetadata,41 AcalaAssetMetadata,42 MoonbeamAssetInfo,43 DemocracyStandardAccountVote,44 IEthCrossAccountId,45} from './types';46import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';47import type {Vec} from '@polkadot/types-codec';48import {FrameSystemEventRecord} from '@polkadot/types/lookup';4950export class CrossAccountId implements ICrossAccountId {51 Substrate?: TSubstrateAccount;52 Ethereum?: TEthereumAccount;5354 constructor(account: ICrossAccountId) {55 if (account.Substrate) this.Substrate = account.Substrate;56 if (account.Ethereum) this.Ethereum = account.Ethereum;57 }5859 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {60 switch (domain) {61 case 'Substrate': return new CrossAccountId({Substrate: account.address});62 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();63 }64 }6566 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {67 return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});68 }6970 static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {71 return encodeAddress(decodeAddress(address), ss58Format);72 }7374 static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {75 return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});76 }7778 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {79 if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);80 return this;81 }8283 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {84 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));85 }8687 toEthereum(): CrossAccountId {88 if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});89 return this;90 }9192 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {93 return evmToAddress(address, ss58Format);94 }9596 toSubstrate(ss58Format?: number): CrossAccountId {97 if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});98 return this;99 }100101 toLowerCase(): CrossAccountId {102 if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();103 if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();104 return this;105 }106}107108const nesting = {109 toChecksumAddress(address: string): string {110 if (typeof address === 'undefined') return '';111112 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);113114 address = address.toLowerCase().replace(/^0x/i,'');115 const addressHash = keccakAsHex(address).replace(/^0x/i,'');116 const checksumAddress = ['0x'];117118 for (let i = 0; i < address.length; i++) {119 120 if (parseInt(addressHash[i], 16) > 7) {121 checksumAddress.push(address[i].toUpperCase());122 } else {123 checksumAddress.push(address[i]);124 }125 }126 return checksumAddress.join('');127 },128 tokenIdToAddress(collectionId: number, tokenId: number) {129 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);130 },131};132133class UniqueUtil {134 static transactionStatus = {135 NOT_READY: 'NotReady',136 FAIL: 'Fail',137 SUCCESS: 'Success',138 };139140 static chainLogType = {141 EXTRINSIC: 'extrinsic',142 RPC: 'rpc',143 };144145 static getTokenAccount(token: IToken): CrossAccountId {146 return new CrossAccountId({Ethereum: this.getTokenAddress(token)});147 }148149 static getTokenAddress(token: IToken): string {150 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);151 }152153 static getDefaultLogger(): ILogger {154 return {155 log(msg: any, level = 'INFO') {156 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));157 },158 level: {159 ERROR: 'ERROR',160 WARNING: 'WARNING',161 INFO: 'INFO',162 },163 };164 }165166 static vec2str(arr: string[] | number[]) {167 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');168 }169170 static str2vec(string: string) {171 if (typeof string !== 'string') return string;172 return Array.from(string).map(x => x.charCodeAt(0));173 }174175 static fromSeed(seed: string, ss58Format = 42) {176 const keyring = new Keyring({type: 'sr25519', ss58Format});177 return keyring.addFromUri(seed);178 }179180 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {181 if (creationResult.status !== this.transactionStatus.SUCCESS) {182 throw Error('Unable to create collection!');183 }184185 let collectionId = null;186 creationResult.result.events.forEach(({event: {data, method, section}}) => {187 if ((section === 'common') && (method === 'CollectionCreated')) {188 collectionId = parseInt(data[0].toString(), 10);189 }190 });191192 if (collectionId === null) {193 throw Error('No CollectionCreated event was found!');194 }195196 return collectionId;197 }198199 static extractTokensFromCreationResult(creationResult: ITransactionResult): {200 success: boolean,201 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],202 } {203 if (creationResult.status !== this.transactionStatus.SUCCESS) {204 throw Error('Unable to create tokens!');205 }206 let success = false;207 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];208 creationResult.result.events.forEach(({event: {data, method, section}}) => {209 if (method === 'ExtrinsicSuccess') {210 success = true;211 } else if ((section === 'common') && (method === 'ItemCreated')) {212 tokens.push({213 collectionId: parseInt(data[0].toString(), 10),214 tokenId: parseInt(data[1].toString(), 10),215 owner: data[2].toHuman(),216 amount: data[3].toBigInt(),217 });218 }219 });220 return {success, tokens};221 }222223 static extractTokensFromBurnResult(burnResult: ITransactionResult): {224 success: boolean,225 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],226 } {227 if (burnResult.status !== this.transactionStatus.SUCCESS) {228 throw Error('Unable to burn tokens!');229 }230 let success = false;231 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];232 burnResult.result.events.forEach(({event: {data, method, section}}) => {233 if (method === 'ExtrinsicSuccess') {234 success = true;235 } else if ((section === 'common') && (method === 'ItemDestroyed')) {236 tokens.push({237 collectionId: parseInt(data[0].toString(), 10),238 tokenId: parseInt(data[1].toString(), 10),239 owner: data[2].toHuman(),240 amount: data[3].toBigInt(),241 });242 }243 });244 return {success, tokens};245 }246247 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {248 let eventId = null;249 events.forEach(({event: {data, method, section}}) => {250 if ((section === expectedSection) && (method === expectedMethod)) {251 eventId = parseInt(data[0].toString(), 10);252 }253 });254255 if (eventId === null) {256 throw Error(`No ${expectedMethod} event was found!`);257 }258 return eventId === collectionId;259 }260261 static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {262 const normalizeAddress = (address: string | ICrossAccountId) => {263 if(typeof address === 'string') return address;264 const obj = {} as any;265 Object.keys(address).forEach(k => {266 obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];267 });268 if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);269 if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();270 return address;271 };272 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;273 events.forEach(({event: {data, method, section}}) => {274 if ((section === 'common') && (method === 'Transfer')) {275 const hData = (data as any).toJSON();276 transfer = {277 collectionId: hData[0],278 tokenId: hData[1],279 from: normalizeAddress(hData[2]),280 to: normalizeAddress(hData[3]),281 amount: BigInt(hData[4]),282 };283 }284 });285 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;286 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);287 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);288 isSuccess = isSuccess && amount === transfer.amount;289 return isSuccess;290 }291292 static bigIntToDecimals(number: bigint, decimals = 18) {293 const numberStr = number.toString();294 const dotPos = numberStr.length - decimals;295296 if (dotPos <= 0) {297 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;298 } else {299 const intPart = numberStr.substring(0, dotPos);300 const fractPart = numberStr.substring(dotPos);301 return intPart + '.' + fractPart;302 }303 }304}305306class UniqueEventHelper {307 private static extractIndex(index: any): [number, number] | string {308 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];309 return index.toJSON();310 }311312 private static extractSub(data: any, subTypes: any): {[key: string]: any} {313 let obj: any = {};314 let index = 0;315316 if (data.entries) {317 for(const [key, value] of data.entries()) {318 obj[key] = this.extractData(value, subTypes[index]);319 index++;320 }321 } else obj = data.toJSON();322323 return obj;324 }325326 private static toHuman(data: any) {327 return data && data.toHuman ? data.toHuman() : `${data}`;328 }329330 private static extractData(data: any, type: any): any {331 if(!type) return this.toHuman(data);332 if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();333 if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();334 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);335 return this.toHuman(data);336 }337338 public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {339 const parsedEvents: IEvent[] = [];340341 events.forEach((record) => {342 const {event, phase} = record;343 const types = event.typeDef;344345 const eventData: IEvent = {346 section: event.section.toString(),347 method: event.method.toString(),348 index: this.extractIndex(event.index),349 data: [],350 phase: phase.toJSON(),351 };352353 event.data.forEach((val: any, index: number) => {354 eventData.data.push(this.extractData(val, types[index]));355 });356357 parsedEvents.push(eventData);358 });359360 return parsedEvents;361 }362}363364export class ChainHelperBase {365 helperBase: any;366367 transactionStatus = UniqueUtil.transactionStatus;368 chainLogType = UniqueUtil.chainLogType;369 util: typeof UniqueUtil;370 eventHelper: typeof UniqueEventHelper;371 logger: ILogger;372 api: ApiPromise | null;373 forcedNetwork: TNetworks | null;374 network: TNetworks | null;375 chainLog: IUniqueHelperLog[];376 children: ChainHelperBase[];377 address: AddressGroup;378 chain: ChainGroup;379380 constructor(logger?: ILogger, helperBase?: any) {381 this.helperBase = helperBase;382383 this.util = UniqueUtil;384 this.eventHelper = UniqueEventHelper;385 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();386 this.logger = logger;387 this.api = null;388 this.forcedNetwork = null;389 this.network = null;390 this.chainLog = [];391 this.children = [];392 this.address = new AddressGroup(this);393 this.chain = new ChainGroup(this);394 }395396 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {397 Object.setPrototypeOf(helperCls.prototype, this);398 const newHelper = new helperCls(this.logger, options);399400 newHelper.api = this.api;401 newHelper.network = this.network;402 newHelper.forceNetwork = this.forceNetwork;403404 this.children.push(newHelper);405406 return newHelper;407 }408409 getApi(): ApiPromise {410 if(this.api === null) throw Error('API not initialized');411 return this.api;412 }413414 async subscribeEvents(expectedEvents: {section: string, names: string[]}[]) {415 const collectedEvents: IEvent[] = [];416 const unsubscribe = await this.getApi().query.system.events((events: Vec<FrameSystemEventRecord>) => {417 const ievents = this.eventHelper.extractEvents(events);418 ievents.forEach((event) => {419 expectedEvents.forEach((e => {420 if (event.section === e.section && e.names.includes(event.method)) {421 collectedEvents.push(event);422 }423 }));424 });425 });426 return {unsubscribe: unsubscribe as any, collectedEvents};427 }428429 clearChainLog(): void {430 this.chainLog = [];431 }432433 forceNetwork(value: TNetworks): void {434 this.forcedNetwork = value;435 }436437 async connect(wsEndpoint: string, listeners?: IApiListeners) {438 if (this.api !== null) throw Error('Already connected');439 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);440 this.api = api;441 this.network = network;442 }443444 async disconnect() {445 for (const child of this.children) {446 child.clearApi();447 }448449 if (this.api === null) return;450 await this.api.disconnect();451 this.clearApi();452 }453454 clearApi() {455 this.api = null;456 this.network = null;457 }458459 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {460 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;461 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];462463 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;464465 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;466 return 'opal';467 }468469 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {470 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});471 await api.isReady;472473 const network = await this.detectNetwork(api);474475 await api.disconnect();476477 return network;478 }479480 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{481 api: ApiPromise;482 network: TNetworks;483 }> {484 if(typeof network === 'undefined' || network === null) network = 'opal';485 const supportedRPC = {486 opal: {487 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,488 },489 quartz: {490 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,491 },492 unique: {493 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,494 },495 rococo: {},496 westend: {},497 moonbeam: {},498 moonriver: {},499 acala: {},500 karura: {},501 westmint: {},502 };503 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);504 const rpc = supportedRPC[network];505506 507 508509 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});510511 await api.isReadyOrError;512513 if (typeof listeners === 'undefined') listeners = {};514 for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {515 if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;516 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);517 }518519 return {api, network};520 }521522 getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {523 const {events, status} = data;524 if (status.isReady) {525 return this.transactionStatus.NOT_READY;526 }527 if (status.isBroadcast) {528 return this.transactionStatus.NOT_READY;529 }530 if (status.isInBlock || status.isFinalized) {531 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');532 if (errors.length > 0) {533 return this.transactionStatus.FAIL;534 }535 if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {536 return this.transactionStatus.SUCCESS;537 }538 }539540 return this.transactionStatus.FAIL;541 }542543 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {544 const sign = (callback: any) => {545 if(options !== null) return transaction.signAndSend(sender, options, callback);546 return transaction.signAndSend(sender, callback);547 };548 549 return new Promise(async (resolve, reject) => {550 try {551 const unsub = await sign((result: any) => {552 const status = this.getTransactionStatus(result);553554 if (status === this.transactionStatus.SUCCESS) {555 this.logger.log(`${label} successful`);556 unsub();557 resolve({result, status});558 } else if (status === this.transactionStatus.FAIL) {559 let moduleError = null;560561 if (result.hasOwnProperty('dispatchError')) {562 const dispatchError = result['dispatchError'];563564 if (dispatchError) {565 if (dispatchError.isModule) {566 const modErr = dispatchError.asModule;567 const errorMeta = dispatchError.registry.findMetaError(modErr);568569 moduleError = `${errorMeta.section}.${errorMeta.name}`;570 } else {571 moduleError = dispatchError.toHuman();572 }573 } else {574 this.logger.log(result, this.logger.level.ERROR);575 }576 }577578 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);579 unsub();580 reject({status, moduleError, result});581 }582 });583 } catch (e) {584 this.logger.log(e, this.logger.level.ERROR);585 reject(e);586 }587 });588 }589590 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {591 const api = this.getApi();592 const signingInfo = await api.derive.tx.signingInfo(signer.address);593594 595 596 tx.sign(signer, {597 blockHash: api.genesisHash,598 genesisHash: api.genesisHash,599 runtimeVersion: api.runtimeVersion,600 nonce: signingInfo.nonce,601 });602603 if (len === null) {604 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;605 } else {606 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;607 }608 }609610 constructApiCall(apiCall: string, params: any[]) {611 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);612 let call = this.getApi() as any;613 for(const part of apiCall.slice(4).split('.')) {614 call = call[part];615 }616 return call(...params);617 }618619 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null) {620 if(this.api === null) throw Error('API not initialized');621 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);622623 const startTime = (new Date()).getTime();624 let result: ITransactionResult;625 let events: IEvent[] = [];626 try {627 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;628 events = this.eventHelper.extractEvents(result.result.events);629 }630 catch(e) {631 if(!(e as object).hasOwnProperty('status')) throw e;632 result = e as ITransactionResult;633 }634635 const endTime = (new Date()).getTime();636637 const log = {638 executedAt: endTime,639 executionTime: endTime - startTime,640 type: this.chainLogType.EXTRINSIC,641 status: result.status,642 call: extrinsic,643 signer: this.getSignerAddress(sender),644 params,645 } as IUniqueHelperLog;646647 if(result.status !== this.transactionStatus.SUCCESS) {648 if (result.moduleError) log.moduleError = result.moduleError;649 else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;650 }651 if(events.length > 0) log.events = events;652653 this.chainLog.push(log);654655 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {656 if (result.moduleError) throw Error(`${result.moduleError}`);657 else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));658 }659 return result;660 }661662 async callRpc(rpc: string, params?: any[]) {663 if(typeof params === 'undefined') params = [];664 if(this.api === null) throw Error('API not initialized');665 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);666667 const startTime = (new Date()).getTime();668 let result;669 let error = null;670 const log = {671 type: this.chainLogType.RPC,672 call: rpc,673 params,674 } as IUniqueHelperLog;675676 try {677 result = await this.constructApiCall(rpc, params);678 }679 catch(e) {680 error = e;681 }682683 const endTime = (new Date()).getTime();684685 log.executedAt = endTime;686 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';687 log.executionTime = endTime - startTime;688689 this.chainLog.push(log);690691 if(error !== null) throw error;692693 return result;694 }695696 getSignerAddress(signer: IKeyringPair | string): string {697 if(typeof signer === 'string') return signer;698 return signer.address;699 }700701 fetchAllPalletNames(): string[] {702 if(this.api === null) throw Error('API not initialized');703 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());704 }705706 fetchMissingPalletNames(requiredPallets: string[]): string[] {707 const palletNames = this.fetchAllPalletNames();708 return requiredPallets.filter(p => !palletNames.includes(p));709 }710}711712713class HelperGroup<T extends ChainHelperBase> {714 helper: T;715716 constructor(uniqueHelper: T) {717 this.helper = uniqueHelper;718 }719}720721722class CollectionGroup extends HelperGroup<UniqueHelper> {723 724725726727728729730731732 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {733 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();734 }735736 737738739740741 async getTotalCount(): Promise<number> {742 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();743 }744745 746747748749750751752753754 async getData(collectionId: number): Promise<{755 id: number;756 name: string;757 description: string;758 tokensCount: number;759 admins: CrossAccountId[];760 normalizedOwner: TSubstrateAccount;761 raw: any762 } | null> {763 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);764 const humanCollection = collection.toHuman(), collectionData = {765 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],766 raw: humanCollection,767 } as any, jsonCollection = collection.toJSON();768 if (humanCollection === null) return null;769 collectionData.raw.limits = jsonCollection.limits;770 collectionData.raw.permissions = jsonCollection.permissions;771 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);772 for (const key of ['name', 'description']) {773 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);774 }775776 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))777 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)778 : 0;779 collectionData.admins = await this.getAdmins(collectionId);780781 return collectionData;782 }783784 785786787788789790791792 async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {793 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();794795 return normalize796 ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())797 : admins;798 }799800 801802803804805806807 async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {808 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();809 return normalize810 ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())811 : allowListed;812 }813814 815816817818819820821 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {822 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();823 }824825 826827828829830831832833 async burn(signer: TSigner, collectionId: number): Promise<boolean> {834 const result = await this.helper.executeExtrinsic(835 signer,836 'api.tx.unique.destroyCollection', [collectionId],837 true,838 );839840 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');841 }842843 844845846847848849850851852 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {853 const result = await this.helper.executeExtrinsic(854 signer,855 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],856 true,857 );858859 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');860 }861862 863864865866867868869870 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {871 const result = await this.helper.executeExtrinsic(872 signer,873 'api.tx.unique.confirmSponsorship', [collectionId],874 true,875 );876877 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');878 }879880 881882883884885886887888 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {889 const result = await this.helper.executeExtrinsic(890 signer,891 'api.tx.unique.removeCollectionSponsor', [collectionId],892 true,893 );894895 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');896 }897898 899900901902903904905906907908909910911912913914915 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {916 const result = await this.helper.executeExtrinsic(917 signer,918 'api.tx.unique.setCollectionLimits', [collectionId, limits],919 true,920 );921922 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');923 }924925 926927928929930931932933934 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {935 const result = await this.helper.executeExtrinsic(936 signer,937 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],938 true,939 );940941 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');942 }943944 945946947948949950951952953 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {954 const result = await this.helper.executeExtrinsic(955 signer,956 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],957 true,958 );959960 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');961 }962963 964965966967968969970971972 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {973 const result = await this.helper.executeExtrinsic(974 signer,975 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],976 true,977 );978979 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');980 }981982 983984985986987988989990 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {991 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();992 }993994 99599699799899910001001 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1002 const result = await this.helper.executeExtrinsic(1003 signer,1004 'api.tx.unique.addToAllowList', [collectionId, addressObj],1005 true,1006 );10071008 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');1009 }10101011 10121013101410151016101710181019 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1020 const result = await this.helper.executeExtrinsic(1021 signer,1022 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1023 true,1024 );10251026 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');1027 }10281029 103010311032103310341035103610371038 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1039 const result = await this.helper.executeExtrinsic(1040 signer,1041 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1042 true,1043 );10441045 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');1046 }10471048 104910501051105210531054105510561057 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1058 return await this.setPermissions(signer, collectionId, {nesting: permissions});1059 }10601061 10621063106410651066106710681069 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1070 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1071 }10721073 107410751076107710781079108010811082 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1083 const result = await this.helper.executeExtrinsic(1084 signer,1085 'api.tx.unique.setCollectionProperties', [collectionId, properties],1086 true,1087 );10881089 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1090 }10911092 10931094109510961097109810991100 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1101 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1102 }11031104 async getPropertiesConsumedSpace(collectionId: number): Promise<number> {1105 const api = this.helper.getApi();1106 const props = (await api.query.common.collectionProperties(collectionId)).toJSON();1107 1108 return (props! as any).consumedSpace;1109 }11101111 async getCollectionOptions(collectionId: number) {1112 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1113 }11141115 111611171118111911201121112211231124 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1125 const result = await this.helper.executeExtrinsic(1126 signer,1127 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1128 true,1129 );11301131 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1132 }11331134 11351136113711381139114011411142114311441145 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1146 const result = await this.helper.executeExtrinsic(1147 signer,1148 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1149 true, 1150 );11511152 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1153 }11541155 1156115711581159116011611162116311641165116611671168 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1169 const result = await this.helper.executeExtrinsic(1170 signer,1171 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1172 true, 1173 );1174 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1175 }11761177 11781179118011811182118311841185118611871188 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1189 const burnResult = await this.helper.executeExtrinsic(1190 signer,1191 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1192 true, 1193 );1194 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1195 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1196 return burnedTokens.success;1197 }11981199 12001201120212031204120512061207120812091210 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1211 const burnResult = await this.helper.executeExtrinsic(1212 signer,1213 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1214 true, 1215 );1216 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1217 return burnedTokens.success && burnedTokens.tokens.length > 0;1218 }12191220 1221122212231224122512261227122812291230 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1231 const approveResult = await this.helper.executeExtrinsic(1232 signer,1233 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1234 true, 1235 );12361237 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1238 }12391240 1241124212431244124512461247124812491250 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1251 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1252 }12531254 1255125612571258125912601261 async getLastTokenId(collectionId: number): Promise<number> {1262 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1263 }12641265 12661267126812691270127112721273 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1274 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1275 }1276}12771278class NFTnRFT extends CollectionGroup {1279 12801281128212831284128512861287 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1288 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1289 }12901291 1292129312941295129612971298129913001301 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1302 properties: IProperty[];1303 owner: CrossAccountId;1304 normalizedOwner: CrossAccountId;1305 }| null> {1306 let tokenData;1307 if(typeof blockHashAt === 'undefined') {1308 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1309 }1310 else {1311 if(propertyKeys.length == 0) {1312 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1313 if(!collection) return null;1314 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1315 }1316 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1317 }1318 tokenData = tokenData.toHuman();1319 if (tokenData === null || tokenData.owner === null) return null;1320 const owner = {} as any;1321 for (const key of Object.keys(tokenData.owner)) {1322 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1323 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1324 : tokenData.owner[key];1325 }1326 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1327 return tokenData;1328 }13291330 13311332133313341335133613371338133913401341 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1342 const result = await this.helper.executeExtrinsic(1343 signer,1344 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1345 true,1346 );13471348 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1349 }13501351 13521353135413551356135713581359 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1360 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1361 }13621363 1364136513661367136813691370137113721373 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1374 const result = await this.helper.executeExtrinsic(1375 signer,1376 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1377 true,1378 );13791380 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1381 }13821383 138413851386138713881389139013911392 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1393 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1394 }13951396 139713981399140014011402140314041405 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1406 const result = await this.helper.executeExtrinsic(1407 signer,1408 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1409 true,1410 );14111412 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1413 }14141415 141614171418141914201421142214231424 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1425 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1426 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1427 for (const key of ['name', 'description', 'tokenPrefix']) {1428 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);1429 }1430 const creationResult = await this.helper.executeExtrinsic(1431 signer,1432 'api.tx.unique.createCollectionEx', [collectionOptions],1433 true, 1434 );1435 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1436 }14371438 getCollectionObject(_collectionId: number): any {1439 return null;1440 }14411442 getTokenObject(_collectionId: number, _tokenId: number): any {1443 return null;1444 }14451446 1447144814491450145114521453 async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1454 return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1455 }14561457 145814591460146114621463 async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1464 const result = await this.helper.executeExtrinsic(1465 signer,1466 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1467 true,1468 );1469 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1470 }1471}147214731474class NFTGroup extends NFTnRFT {1475 147614771478147914801481 getCollectionObject(collectionId: number): UniqueNFTCollection {1482 return new UniqueNFTCollection(collectionId, this.helper);1483 }14841485 1486148714881489149014911492 getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1493 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1494 }14951496 14971498149915001501150215031504 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1505 let owner;1506 if (typeof blockHashAt === 'undefined') {1507 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1508 } else {1509 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1510 }1511 return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1512 }15131514 1515151615171518151915201521 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1522 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1523 }15241525 1526152715281529153015311532153315341535 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1536 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1537 }15381539 154015411542154315441545154615471548154915501551 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1552 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1553 }15541555 15561557155815591560156115621563 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1564 let owner;1565 if (typeof blockHashAt === 'undefined') {1566 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1567 } else {1568 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1569 }15701571 if (owner === null) return null;15721573 return owner.toHuman();1574 }15751576 15771578157915801581158215831584 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1585 let children;1586 if(typeof blockHashAt === 'undefined') {1587 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1588 } else {1589 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1590 }15911592 return children.toJSON().map((x: any) => {1593 return {collectionId: x.collection, tokenId: x.token};1594 });1595 }15961597 15981599160016011602160316041605 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1606 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1607 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1608 if(!result) {1609 throw Error('Unable to nest token!');1610 }1611 return result;1612 }16131614 161516161617161816191620162116221623 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1624 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1625 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1626 if(!result) {1627 throw Error('Unable to unnest token!');1628 }1629 return result;1630 }16311632 163316341635163616371638163916401641164216431644 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1645 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1646 }16471648 164916501651165216531654 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1655 const creationResult = await this.helper.executeExtrinsic(1656 signer,1657 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1658 nft: {1659 properties: data.properties,1660 },1661 }],1662 true,1663 );1664 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1665 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1666 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1667 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1668 }16691670 167116721673167416751676167716781679168016811682168316841685 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1686 const creationResult = await this.helper.executeExtrinsic(1687 signer,1688 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1689 true,1690 );1691 const collection = this.getCollectionObject(collectionId);1692 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1693 }16941695 169616971698169917001701170217031704170517061707170817091710171117121713 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1714 const rawTokens = [];1715 for (const token of tokens) {1716 const raw = {NFT: {properties: token.properties}};1717 rawTokens.push(raw);1718 }1719 const creationResult = await this.helper.executeExtrinsic(1720 signer,1721 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1722 true,1723 );1724 const collection = this.getCollectionObject(collectionId);1725 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1726 }17271728 1729173017311732173317341735173617371738 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1739 return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1740 }1741}174217431744class RFTGroup extends NFTnRFT {1745 174617471748174917501751 getCollectionObject(collectionId: number): UniqueRFTCollection {1752 return new UniqueRFTCollection(collectionId, this.helper);1753 }17541755 1756175717581759176017611762 getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1763 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1764 }17651766 1767176817691770177117721773 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1774 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1775 }17761777 17781779178017811782178317841785 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1786 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1787 }17881789 1790179117921793179417951796179717981799 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1800 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1801 }18021803 18041805180618071808180918101811181218131814 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1815 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1816 }18171818 181918201821182218231824182518261827182818291830 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1831 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1832 }18331834 1835183618371838183918401841 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1842 const creationResult = await this.helper.executeExtrinsic(1843 signer,1844 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1845 refungible: {1846 pieces: data.pieces,1847 properties: data.properties,1848 },1849 }],1850 true,1851 );1852 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1853 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1854 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1855 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1856 }18571858 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1859 throw Error('Not implemented');1860 const creationResult = await this.helper.executeExtrinsic(1861 signer,1862 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1863 true, 1864 );1865 const collection = this.getCollectionObject(collectionId);1866 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1867 }18681869 187018711872187318741875187618771878 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1879 const rawTokens = [];1880 for (const token of tokens) {1881 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1882 rawTokens.push(raw);1883 }1884 const creationResult = await this.helper.executeExtrinsic(1885 signer,1886 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1887 true,1888 );1889 const collection = this.getCollectionObject(collectionId);1890 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1891 }18921893 189418951896189718981899190019011902 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1903 return await super.burnToken(signer, collectionId, tokenId, amount);1904 }19051906 1907190819091910191119121913191419151916 async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1917 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1918 }19191920 19211922192319241925192619271928192919301931 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1932 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1933 }19341935 1936193719381939194019411942 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1943 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1944 }19451946 194719481949195019511952195319541955 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1956 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1957 const repartitionResult = await this.helper.executeExtrinsic(1958 signer,1959 'api.tx.unique.repartition', [collectionId, tokenId, amount],1960 true,1961 );1962 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1963 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1964 }1965}196619671968class FTGroup extends CollectionGroup {1969 197019711972197319741975 getCollectionObject(collectionId: number): UniqueFTCollection {1976 return new UniqueFTCollection(collectionId, this.helper);1977 }19781979 1980198119821983198419851986198719881989199019911992 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1993 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1994 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1995 collectionOptions.mode = {fungible: decimalPoints};1996 for (const key of ['name', 'description', 'tokenPrefix']) {1997 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);1998 }1999 const creationResult = await this.helper.executeExtrinsic(2000 signer,2001 'api.tx.unique.createCollectionEx', [collectionOptions],2002 true,2003 );2004 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));2005 }20062007 200820092010201120122013201420152016 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {2017 const creationResult = await this.helper.executeExtrinsic(2018 signer,2019 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {2020 fungible: {2021 value: amount,2022 },2023 }],2024 true, 2025 );2026 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2027 }20282029 20302031203220332034203520362037 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {2038 const rawTokens = [];2039 for (const token of tokens) {2040 const raw = {Fungible: {Value: token.value}};2041 rawTokens.push(raw);2042 }2043 const creationResult = await this.helper.executeExtrinsic(2044 signer,2045 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2046 true,2047 );2048 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2049 }20502051 205220532054205520562057 async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2058 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2059 }20602061 2062206320642065206620672068 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2069 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2070 }20712072 207320742075207620772078207920802081 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2082 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2083 }20842085 2086208720882089209020912092209320942095 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2096 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2097 }20982099 21002101210221032104210521062107 async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2108 return await super.burnToken(signer, collectionId, 0, amount);2109 }21102111 211221132114211521162117211821192120 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2121 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2122 }21232124 21252126212721282129 async getTotalPieces(collectionId: number): Promise<bigint> {2130 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2131 }21322133 2134213521362137213821392140214121422143 approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2144 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2145 }21462147 2148214921502151215221532154 getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2155 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2156 }2157}215821592160class ChainGroup extends HelperGroup<ChainHelperBase> {2161 21622163216421652166 getChainProperties(): IChainProperties {2167 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2168 return {2169 ss58Format: properties.ss58Format.toJSON(),2170 tokenDecimals: properties.tokenDecimals.toJSON(),2171 tokenSymbol: properties.tokenSymbol.toJSON(),2172 };2173 }21742175 21762177217821792180 async getLatestBlockNumber(): Promise<number> {2181 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2182 }21832184 218521862187218821892190 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2191 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2192 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2193 return blockHash;2194 }21952196 2197 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2198 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2199 if (!blockHash) return null;2200 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2201 }22022203 220422052206220722082209 async getNonce(address: TSubstrateAccount): Promise<number> {2210 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2211 }2212}22132214class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2215 221622172218221922202221 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2222 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2223 }22242225 22262227222822292230223122322233 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2234 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22352236 let transfer = {from: null, to: null, amount: 0n} as any;2237 result.result.events.forEach(({event: {data, method, section}}) => {2238 if ((section === 'balances') && (method === 'Transfer')) {2239 transfer = {2240 from: this.helper.address.normalizeSubstrate(data[0]),2241 to: this.helper.address.normalizeSubstrate(data[1]),2242 amount: BigInt(data[2]),2243 };2244 }2245 });2246 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2247 && this.helper.address.normalizeSubstrate(address) === transfer.to2248 && BigInt(amount) === transfer.amount;2249 return isSuccess;2250 }22512252 22532254225522562257 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2258 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2259 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2260 }2261}22622263class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2264 226522662267226822692270 async getEthereum(address: TEthereumAccount): Promise<bigint> {2271 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2272 }22732274 22752276227722782279228022812282 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2283 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22842285 let transfer = {from: null, to: null, amount: 0n} as any;2286 result.result.events.forEach(({event: {data, method, section}}) => {2287 if ((section === 'balances') && (method === 'Transfer')) {2288 transfer = {2289 from: data[0].toString(),2290 to: data[1].toString(),2291 amount: BigInt(data[2]),2292 };2293 }2294 });2295 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2296 && address === transfer.to2297 && BigInt(amount) === transfer.amount;2298 return isSuccess;2299 }2300}23012302class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2303 subBalanceGroup: SubstrateBalanceGroup<T>;2304 ethBalanceGroup: EthereumBalanceGroup<T>;23052306 constructor(helper: T) {2307 super(helper);2308 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2309 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2310 }23112312 getCollectionCreationPrice(): bigint {2313 return 2n * this.getOneTokenNominal();2314 }2315 23162317231823192320 getOneTokenNominal(): bigint {2321 const chainProperties = this.helper.chain.getChainProperties();2322 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2323 }23242325 232623272328232923302331 getSubstrate(address: TSubstrateAccount): Promise<bigint> {2332 return this.subBalanceGroup.getSubstrate(address);2333 }23342335 23362337233823392340 getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2341 return this.subBalanceGroup.getSubstrateFull(address);2342 }23432344 234523462347234823492350 getEthereum(address: TEthereumAccount): Promise<bigint> {2351 return this.ethBalanceGroup.getEthereum(address);2352 }23532354 23552356235723582359236023612362 transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2363 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2364 }23652366 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2367 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);23682369 let transfer = {from: null, to: null, amount: 0n} as any;2370 result.result.events.forEach(({event: {data, method, section}}) => {2371 if ((section === 'balances') && (method === 'Transfer')) {2372 transfer = {2373 from: this.helper.address.normalizeSubstrate(data[0]),2374 to: this.helper.address.normalizeSubstrate(data[1]),2375 amount: BigInt(data[2]),2376 };2377 }2378 });2379 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2380 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2381 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2382 return isSuccess;2383 }2384}23852386class AddressGroup extends HelperGroup<ChainHelperBase> {2387 2388238923902391239223932394 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2395 return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2396 }23972398 239924002401240224032404 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2405 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2406 }24072408 2409241024112412241324142415 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2416 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2417 }24182419 242024212422242324242425 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2426 return CrossAccountId.translateSubToEth(subAddress);2427 }24282429 243024312432243324342435 encodeSubstrateAddress (key: Uint8Array | string | bigint, ss58Format = 42): string {2436 const u8a :Uint8Array = typeof key === 'string'2437 ? hexToU8a(key)2438 : typeof key === 'bigint'2439 ? hexToU8a(key.toString(16))2440 : key;2441 2442 if (ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2443 throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2444 }2445 2446 const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2447 if (!allowedDecodedLengths.includes(u8a.length)) {2448 throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2449 }2450 2451 const u8aPrefix = ss58Format < 642452 ? new Uint8Array([ss58Format])2453 : new Uint8Array([2454 ((ss58Format & 0xfc) >> 2) | 0x40,2455 (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2456 ]);24572458 const input = u8aConcat(u8aPrefix, u8a);2459 2460 return base58Encode(u8aConcat(2461 input,2462 blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2463 ));2464 }24652466 24672468246924702471 restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2472 if (this.helper.api === null) {2473 throw 'Not connected';2474 }2475 const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2476 if (res === undefined || res === null) {2477 throw 'Restore address error';2478 }2479 return res.toString();2480 }24812482 24832484248524862487 convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2488 if (ethCrossAccount.sub === '0') {2489 return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2490 }2491 2492 const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2493 return {Substrate: ss58};2494 }24952496 paraSiblingSovereignAccount(paraid: number) {2497 2498 2499 const siblingPrefix = '0x7369626c';25002501 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2502 const suffix = '000000000000000000000000000000000000000000000000';25032504 return siblingPrefix + encodedParaId + suffix;2505 }2506}25072508class StakingGroup extends HelperGroup<UniqueHelper> {2509 2510251125122513251425152516 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2517 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2518 const _stakeResult = await this.helper.executeExtrinsic(2519 signer, 'api.tx.appPromotion.stake',2520 [amountToStake], true,2521 );2522 2523 return true;2524 }25252526 2527252825292530253125322533 async unstake(signer: TSigner, label?: string): Promise<number> {2534 if(typeof label === 'undefined') label = `${signer.address}`;2535 const _unstakeResult = await this.helper.executeExtrinsic(2536 signer, 'api.tx.appPromotion.unstake',2537 [], true,2538 );2539 2540 return 1;2541 }25422543 25442545254625472548 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2549 if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2550 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2551 }25522553 25542555255625572558 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2559 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2560 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2561 return {2562 block: block.toBigInt(),2563 amount: amount.toBigInt(),2564 };2565 });2566 }25672568 25692570257125722573 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2574 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2575 }25762577 25782579258025812582 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2583 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2584 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2585 return {2586 block: block.toBigInt(),2587 amount: amount.toBigInt(),2588 };2589 });2590 return result;2591 }2592}25932594class SchedulerGroup extends HelperGroup<UniqueHelper> {2595 constructor(helper: UniqueHelper) {2596 super(helper);2597 }25982599 cancelScheduled(signer: TSigner, scheduledId: string) {2600 return this.helper.executeExtrinsic(2601 signer,2602 'api.tx.scheduler.cancelNamed',2603 [scheduledId],2604 true,2605 );2606 }26072608 changePriority(signer: TSigner, scheduledId: string, priority: number) {2609 return this.helper.executeExtrinsic(2610 signer,2611 'api.tx.scheduler.changeNamedPriority',2612 [scheduledId, priority],2613 true,2614 );2615 }26162617 scheduleAt<T extends UniqueHelper>(2618 executionBlockNumber: number,2619 options: ISchedulerOptions = {},2620 ) {2621 return this.schedule<T>('schedule', executionBlockNumber, options);2622 }26232624 scheduleAfter<T extends UniqueHelper>(2625 blocksBeforeExecution: number,2626 options: ISchedulerOptions = {},2627 ) {2628 return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);2629 }26302631 schedule<T extends UniqueHelper>(2632 scheduleFn: 'schedule' | 'scheduleAfter',2633 blocksNum: number,2634 options: ISchedulerOptions = {},2635 ) {2636 2637 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2638 return this.helper.clone(ScheduledHelperType, {2639 scheduleFn,2640 blocksNum,2641 options,2642 }) as T;2643 }2644}26452646class CollatorSelectionGroup extends HelperGroup<UniqueHelper> {2647 2648 setKeys(signer: TSigner, key: string) {2649 return this.helper.executeExtrinsic(2650 signer,2651 'api.tx.session.setKeys', 2652 [2653 key,2654 '0x0',2655 ],2656 true,2657 );2658 }26592660 setOwnKeys(signer: TSigner) {2661 return this.setKeys(signer, '0x' + Buffer.from(signer.addressRaw).toString('hex'));2662 }26632664 addInvulnerable(signer: TSigner, address: string) {2665 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.addInvulnerable', [address]);2666 }26672668 removeInvulnerable(signer: TSigner, address: string) {2669 return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.removeInvulnerable', [address]);2670 }26712672 async getInvulnerables() {2673 return (await this.helper.callRpc('api.query.collatorSelection.invulnerables')).map((x: any) => x.toHuman());2674 }2675}26762677class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2678 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2679 await this.helper.executeExtrinsic(2680 signer,2681 'api.tx.foreignAssets.registerForeignAsset',2682 [ownerAddress, location, metadata],2683 true,2684 );2685 }26862687 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2688 await this.helper.executeExtrinsic(2689 signer,2690 'api.tx.foreignAssets.updateForeignAsset',2691 [foreignAssetId, location, metadata],2692 true,2693 );2694 }2695}26962697class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2698 palletName: string;26992700 constructor(helper: T, palletName: string) {2701 super(helper);27022703 this.palletName = palletName;2704 }27052706 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2707 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2708 }2709}27102711class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2712 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2713 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2714 }27152716 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2717 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2718 }27192720 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2721 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2722 }2723}27242725class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2726 async accounts(address: string, currencyId: any) {2727 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2728 return BigInt(free);2729 }2730}27312732class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2733 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2734 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2735 }27362737 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2738 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2739 }27402741 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2742 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2743 }27442745 async account(assetId: string | number, address: string) {2746 const accountAsset = (2747 await this.helper.callRpc('api.query.assets.account', [assetId, address])2748 ).toJSON()! as any;27492750 if (accountAsset !== null) {2751 return BigInt(accountAsset['balance']);2752 } else {2753 return null;2754 }2755 }2756}27572758class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2759 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2760 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2761 }2762}27632764class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2765 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2766 const apiPrefix = 'api.tx.assetManager.';27672768 const registerTx = this.helper.constructApiCall(2769 apiPrefix + 'registerForeignAsset',2770 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2771 );27722773 const setUnitsTx = this.helper.constructApiCall(2774 apiPrefix + 'setAssetUnitsPerSecond',2775 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2776 );27772778 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2779 const encodedProposal = batchCall?.method.toHex() || '';2780 return encodedProposal;2781 }27822783 async assetTypeId(location: any) {2784 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2785 }2786}27872788class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2789 async notePreimage(signer: TSigner, encodedProposal: string) {2790 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2791 }27922793 externalProposeMajority(proposalHash: string) {2794 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2795 }27962797 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2798 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2799 }28002801 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2802 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2803 }2804}28052806class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2807 collective: string;28082809 constructor(helper: MoonbeamHelper, collective: string) {2810 super(helper);28112812 this.collective = collective;2813 }28142815 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2816 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2817 }28182819 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2820 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2821 }28222823 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2824 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2825 }28262827 async proposalCount() {2828 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2829 }2830}28312832export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2833export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;28342835export class UniqueHelper extends ChainHelperBase {2836 balance: BalanceGroup<UniqueHelper>;2837 collection: CollectionGroup;2838 nft: NFTGroup;2839 rft: RFTGroup;2840 ft: FTGroup;2841 staking: StakingGroup;2842 scheduler: SchedulerGroup;2843 collatorSelection: CollatorSelectionGroup;2844 foreignAssets: ForeignAssetsGroup;2845 xcm: XcmGroup<UniqueHelper>;2846 xTokens: XTokensGroup<UniqueHelper>;2847 tokens: TokensGroup<UniqueHelper>;28482849 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2850 super(logger, options.helperBase ?? UniqueHelper);28512852 this.balance = new BalanceGroup(this);2853 this.collection = new CollectionGroup(this);2854 this.nft = new NFTGroup(this);2855 this.rft = new RFTGroup(this);2856 this.ft = new FTGroup(this);2857 this.staking = new StakingGroup(this);2858 this.scheduler = new SchedulerGroup(this);2859 this.collatorSelection = new CollatorSelectionGroup(this);2860 this.foreignAssets = new ForeignAssetsGroup(this);2861 this.xcm = new XcmGroup(this, 'polkadotXcm');2862 this.xTokens = new XTokensGroup(this);2863 this.tokens = new TokensGroup(this);2864 }28652866 getSudo<T extends UniqueHelper>() {2867 2868 const SudoHelperType = SudoHelper(this.helperBase);2869 return this.clone(SudoHelperType) as T;2870 }2871}28722873export class XcmChainHelper extends ChainHelperBase {2874 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2875 const wsProvider = new WsProvider(wsEndpoint);2876 this.api = new ApiPromise({2877 provider: wsProvider,2878 });2879 await this.api.isReadyOrError;2880 this.network = await UniqueHelper.detectNetwork(this.api);2881 }2882}28832884export class RelayHelper extends XcmChainHelper {2885 xcm: XcmGroup<RelayHelper>;28862887 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2888 super(logger, options.helperBase ?? RelayHelper);28892890 this.xcm = new XcmGroup(this, 'xcmPallet');2891 }2892}28932894export class WestmintHelper extends XcmChainHelper {2895 balance: SubstrateBalanceGroup<WestmintHelper>;2896 xcm: XcmGroup<WestmintHelper>;2897 assets: AssetsGroup<WestmintHelper>;2898 xTokens: XTokensGroup<WestmintHelper>;28992900 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2901 super(logger, options.helperBase ?? WestmintHelper);29022903 this.balance = new SubstrateBalanceGroup(this);2904 this.xcm = new XcmGroup(this, 'polkadotXcm');2905 this.assets = new AssetsGroup(this);2906 this.xTokens = new XTokensGroup(this);2907 }2908}29092910export class MoonbeamHelper extends XcmChainHelper {2911 balance: EthereumBalanceGroup<MoonbeamHelper>;2912 assetManager: MoonbeamAssetManagerGroup;2913 assets: AssetsGroup<MoonbeamHelper>;2914 xTokens: XTokensGroup<MoonbeamHelper>;2915 democracy: MoonbeamDemocracyGroup;2916 collective: {2917 council: MoonbeamCollectiveGroup,2918 techCommittee: MoonbeamCollectiveGroup,2919 };29202921 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2922 super(logger, options.helperBase ?? MoonbeamHelper);29232924 this.balance = new EthereumBalanceGroup(this);2925 this.assetManager = new MoonbeamAssetManagerGroup(this);2926 this.assets = new AssetsGroup(this);2927 this.xTokens = new XTokensGroup(this);2928 this.democracy = new MoonbeamDemocracyGroup(this);2929 this.collective = {2930 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2931 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2932 };2933 }2934}29352936export class AcalaHelper extends XcmChainHelper {2937 balance: SubstrateBalanceGroup<AcalaHelper>;2938 assetRegistry: AcalaAssetRegistryGroup;2939 xTokens: XTokensGroup<AcalaHelper>;2940 tokens: TokensGroup<AcalaHelper>;29412942 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2943 super(logger, options.helperBase ?? AcalaHelper);29442945 this.balance = new SubstrateBalanceGroup(this);2946 this.assetRegistry = new AcalaAssetRegistryGroup(this);2947 this.xTokens = new XTokensGroup(this);2948 this.tokens = new TokensGroup(this);2949 }29502951 getSudo<T extends AcalaHelper>() {2952 2953 const SudoHelperType = SudoHelper(this.helperBase);2954 return this.clone(SudoHelperType) as T;2955 }2956}295729582959function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2960 return class extends Base {2961 scheduleFn: 'schedule' | 'scheduleAfter';2962 blocksNum: number;2963 options: ISchedulerOptions;29642965 constructor(...args: any[]) {2966 const logger = args[0] as ILogger;2967 const options = args[1] as {2968 scheduleFn: 'schedule' | 'scheduleAfter',2969 blocksNum: number,2970 options: ISchedulerOptions2971 };29722973 super(logger);29742975 this.scheduleFn = options.scheduleFn;2976 this.blocksNum = options.blocksNum;2977 this.options = options.options;2978 }29792980 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {2981 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);2982 2983 const mandatorySchedArgs = [2984 this.blocksNum,2985 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,2986 this.options.priority ?? null,2987 scheduledTx,2988 ];2989 2990 let schedArgs;2991 let scheduleFn;29922993 if (this.options.scheduledId) {2994 schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];29952996 if (this.scheduleFn == 'schedule') {2997 scheduleFn = 'scheduleNamed';2998 } else if (this.scheduleFn == 'scheduleAfter') {2999 scheduleFn = 'scheduleNamedAfter';3000 }3001 } else {3002 schedArgs = mandatorySchedArgs;3003 scheduleFn = this.scheduleFn;3004 }30053006 const extrinsic = 'api.tx.scheduler.' + scheduleFn;30073008 return super.executeExtrinsic(3009 sender,3010 extrinsic,3011 schedArgs,3012 expectSuccess,3013 );3014 }3015 };3016}301730183019function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {3020 return class extends Base {3021 constructor(...args: any[]) {3022 super(...args);3023 }30243025 async executeExtrinsic(3026 sender: IKeyringPair,3027 extrinsic: string,3028 params: any[],3029 expectSuccess?: boolean,3030 options: Partial<SignerOptions>|null = null,3031 ): Promise<ITransactionResult> {3032 const call = this.constructApiCall(extrinsic, params);3033 const result = await super.executeExtrinsic(3034 sender,3035 'api.tx.sudo.sudo',3036 [call],3037 expectSuccess,3038 options,3039 );30403041 if (result.status === 'Fail') return result;30423043 const data = this.eventHelper.extractEvents(result.result.events).find(x => x.section == 'sudo')?.data[0];3044 if (data.err) {3045 const error = data.err.module;3046 3047 const metaError = super.getApi()?.registry.findMetaError({index: new BN(error.index), error: new BN(9)});3048 throw new Error(`${data.err.module.error} ${metaError.section}.${metaError.name}`);3049 }3050 return result;3051 }3052 };3053}30543055export class UniqueBaseCollection {3056 helper: UniqueHelper;3057 collectionId: number;30583059 constructor(collectionId: number, uniqueHelper: UniqueHelper) {3060 this.collectionId = collectionId;3061 this.helper = uniqueHelper;3062 }30633064 async getData() {3065 return await this.helper.collection.getData(this.collectionId);3066 }30673068 async getLastTokenId() {3069 return await this.helper.collection.getLastTokenId(this.collectionId);3070 }30713072 async doesTokenExist(tokenId: number) {3073 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);3074 }30753076 async getAdmins() {3077 return await this.helper.collection.getAdmins(this.collectionId);3078 }30793080 async getAllowList() {3081 return await this.helper.collection.getAllowList(this.collectionId);3082 }30833084 async getEffectiveLimits() {3085 return await this.helper.collection.getEffectiveLimits(this.collectionId);3086 }30873088 async getProperties(propertyKeys?: string[] | null) {3089 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3090 }30913092 async getPropertiesConsumedSpace() {3093 return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);3094 }30953096 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3097 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3098 }30993100 async getOptions() {3101 return await this.helper.collection.getCollectionOptions(this.collectionId);3102 }31033104 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {3105 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);3106 }31073108 async confirmSponsorship(signer: TSigner) {3109 return await this.helper.collection.confirmSponsorship(signer, this.collectionId);3110 }31113112 async removeSponsor(signer: TSigner) {3113 return await this.helper.collection.removeSponsor(signer, this.collectionId);3114 }31153116 async setLimits(signer: TSigner, limits: ICollectionLimits) {3117 return await this.helper.collection.setLimits(signer, this.collectionId, limits);3118 }31193120 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {3121 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);3122 }31233124 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3125 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);3126 }31273128 async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {3129 return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);3130 }31313132 async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {3133 return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);3134 }31353136 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3137 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);3138 }31393140 async setProperties(signer: TSigner, properties: IProperty[]) {3141 return await this.helper.collection.setProperties(signer, this.collectionId, properties);3142 }31433144 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3145 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);3146 }31473148 async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {3149 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);3150 }31513152 async enableNesting(signer: TSigner, permissions: INestingPermissions) {3153 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);3154 }31553156 async disableNesting(signer: TSigner) {3157 return await this.helper.collection.disableNesting(signer, this.collectionId);3158 }31593160 async burn(signer: TSigner) {3161 return await this.helper.collection.burn(signer, this.collectionId);3162 }31633164 scheduleAt<T extends UniqueHelper>(3165 executionBlockNumber: number,3166 options: ISchedulerOptions = {},3167 ) {3168 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3169 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3170 }31713172 scheduleAfter<T extends UniqueHelper>(3173 blocksBeforeExecution: number,3174 options: ISchedulerOptions = {},3175 ) {3176 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3177 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3178 }31793180 getSudo<T extends UniqueHelper>() {3181 return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());3182 }3183}318431853186export class UniqueNFTCollection extends UniqueBaseCollection {3187 getTokenObject(tokenId: number) {3188 return new UniqueNFToken(tokenId, this);3189 }31903191 async getTokensByAddress(addressObj: ICrossAccountId) {3192 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3193 }31943195 async getToken(tokenId: number, blockHashAt?: string) {3196 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3197 }31983199 async getTokenOwner(tokenId: number, blockHashAt?: string) {3200 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3201 }32023203 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3204 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3205 }32063207 async getTokenChildren(tokenId: number, blockHashAt?: string) {3208 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3209 }32103211 async getPropertyPermissions(propertyKeys: string[] | null = null) {3212 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3213 }32143215 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3216 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3217 }32183219 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3220 const api = this.helper.getApi();3221 const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON();3222 3223 return (props! as any).consumedSpace;3224 }32253226 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3227 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3228 }32293230 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3231 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3232 }32333234 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3235 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3236 }32373238 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3239 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3240 }32413242 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3243 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3244 }32453246 async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3247 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3248 }32493250 async burnToken(signer: TSigner, tokenId: number) {3251 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3252 }32533254 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3255 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3256 }32573258 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3259 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3260 }32613262 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3263 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3264 }32653266 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3267 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3268 }32693270 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3271 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3272 }32733274 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3275 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3276 }32773278 scheduleAt<T extends UniqueHelper>(3279 executionBlockNumber: number,3280 options: ISchedulerOptions = {},3281 ) {3282 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3283 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3284 }32853286 scheduleAfter<T extends UniqueHelper>(3287 blocksBeforeExecution: number,3288 options: ISchedulerOptions = {},3289 ) {3290 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3291 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3292 }32933294 getSudo<T extends UniqueHelper>() {3295 return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3296 }3297}329832993300export class UniqueRFTCollection extends UniqueBaseCollection {3301 getTokenObject(tokenId: number) {3302 return new UniqueRFToken(tokenId, this);3303 }33043305 async getToken(tokenId: number, blockHashAt?: string) {3306 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3307 }33083309 async getTokensByAddress(addressObj: ICrossAccountId) {3310 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3311 }33123313 async getTop10TokenOwners(tokenId: number) {3314 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3315 }33163317 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3318 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3319 }33203321 async getTokenTotalPieces(tokenId: number) {3322 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3323 }33243325 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3326 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3327 }33283329 async getPropertyPermissions(propertyKeys: string[] | null = null) {3330 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3331 }33323333 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3334 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3335 }33363337 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3338 const api = this.helper.getApi();3339 const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON();3340 3341 return (props! as any).consumedSpace;3342 }33433344 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3345 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3346 }33473348 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3349 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3350 }33513352 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3353 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3354 }33553356 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3357 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3358 }33593360 async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3361 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3362 }33633364 async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3365 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3366 }33673368 async burnToken(signer: TSigner, tokenId: number, amount=1n) {3369 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3370 }33713372 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n) {3373 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3374 }33753376 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3377 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3378 }33793380 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3381 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3382 }33833384 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3385 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3386 }33873388 scheduleAt<T extends UniqueHelper>(3389 executionBlockNumber: number,3390 options: ISchedulerOptions = {},3391 ) {3392 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3393 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3394 }33953396 scheduleAfter<T extends UniqueHelper>(3397 blocksBeforeExecution: number,3398 options: ISchedulerOptions = {},3399 ) {3400 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3401 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3402 }34033404 getSudo<T extends UniqueHelper>() {3405 return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3406 }3407}340834093410export class UniqueFTCollection extends UniqueBaseCollection {3411 async getBalance(addressObj: ICrossAccountId) {3412 return await this.helper.ft.getBalance(this.collectionId, addressObj);3413 }34143415 async getTotalPieces() {3416 return await this.helper.ft.getTotalPieces(this.collectionId);3417 }34183419 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3420 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3421 }34223423 async getTop10Owners() {3424 return await this.helper.ft.getTop10Owners(this.collectionId);3425 }34263427 async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3428 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3429 }34303431 async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3432 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3433 }34343435 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3436 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3437 }34383439 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3440 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3441 }34423443 async burnTokens(signer: TSigner, amount=1n) {3444 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3445 }34463447 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3448 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3449 }34503451 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3452 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3453 }34543455 scheduleAt<T extends UniqueHelper>(3456 executionBlockNumber: number,3457 options: ISchedulerOptions = {},3458 ) {3459 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3460 return new UniqueFTCollection(this.collectionId, scheduledHelper);3461 }34623463 scheduleAfter<T extends UniqueHelper>(3464 blocksBeforeExecution: number,3465 options: ISchedulerOptions = {},3466 ) {3467 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3468 return new UniqueFTCollection(this.collectionId, scheduledHelper);3469 }34703471 getSudo<T extends UniqueHelper>() {3472 return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3473 }3474}347534763477export class UniqueBaseToken {3478 collection: UniqueNFTCollection | UniqueRFTCollection;3479 collectionId: number;3480 tokenId: number;34813482 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3483 this.collection = collection;3484 this.collectionId = collection.collectionId;3485 this.tokenId = tokenId;3486 }34873488 async getNextSponsored(addressObj: ICrossAccountId) {3489 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3490 }34913492 async getProperties(propertyKeys?: string[] | null) {3493 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3494 }34953496 async getTokenPropertiesConsumedSpace() {3497 return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);3498 }34993500 async setProperties(signer: TSigner, properties: IProperty[]) {3501 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3502 }35033504 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3505 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3506 }35073508 async doesExist() {3509 return await this.collection.doesTokenExist(this.tokenId);3510 }35113512 nestingAccount() {3513 return this.collection.helper.util.getTokenAccount(this);3514 }35153516 scheduleAt<T extends UniqueHelper>(3517 executionBlockNumber: number,3518 options: ISchedulerOptions = {},3519 ) {3520 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3521 return new UniqueBaseToken(this.tokenId, scheduledCollection);3522 }35233524 scheduleAfter<T extends UniqueHelper>(3525 blocksBeforeExecution: number,3526 options: ISchedulerOptions = {},3527 ) {3528 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3529 return new UniqueBaseToken(this.tokenId, scheduledCollection);3530 }35313532 getSudo<T extends UniqueHelper>() {3533 return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3534 }3535}353635373538export class UniqueNFToken extends UniqueBaseToken {3539 collection: UniqueNFTCollection;35403541 constructor(tokenId: number, collection: UniqueNFTCollection) {3542 super(tokenId, collection);3543 this.collection = collection;3544 }35453546 async getData(blockHashAt?: string) {3547 return await this.collection.getToken(this.tokenId, blockHashAt);3548 }35493550 async getOwner(blockHashAt?: string) {3551 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3552 }35533554 async getTopmostOwner(blockHashAt?: string) {3555 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3556 }35573558 async getChildren(blockHashAt?: string) {3559 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3560 }35613562 async nest(signer: TSigner, toTokenObj: IToken) {3563 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3564 }35653566 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3567 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3568 }35693570 async transfer(signer: TSigner, addressObj: ICrossAccountId) {3571 return await this.collection.transferToken(signer, this.tokenId, addressObj);3572 }35733574 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3575 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3576 }35773578 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3579 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3580 }35813582 async isApproved(toAddressObj: ICrossAccountId) {3583 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3584 }35853586 async burn(signer: TSigner) {3587 return await this.collection.burnToken(signer, this.tokenId);3588 }35893590 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3591 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3592 }35933594 scheduleAt<T extends UniqueHelper>(3595 executionBlockNumber: number,3596 options: ISchedulerOptions = {},3597 ) {3598 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3599 return new UniqueNFToken(this.tokenId, scheduledCollection);3600 }36013602 scheduleAfter<T extends UniqueHelper>(3603 blocksBeforeExecution: number,3604 options: ISchedulerOptions = {},3605 ) {3606 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3607 return new UniqueNFToken(this.tokenId, scheduledCollection);3608 }36093610 getSudo<T extends UniqueHelper>() {3611 return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3612 }3613}36143615export class UniqueRFToken extends UniqueBaseToken {3616 collection: UniqueRFTCollection;36173618 constructor(tokenId: number, collection: UniqueRFTCollection) {3619 super(tokenId, collection);3620 this.collection = collection;3621 }36223623 async getData(blockHashAt?: string) {3624 return await this.collection.getToken(this.tokenId, blockHashAt);3625 }36263627 async getTop10Owners() {3628 return await this.collection.getTop10TokenOwners(this.tokenId);3629 }36303631 async getBalance(addressObj: ICrossAccountId) {3632 return await this.collection.getTokenBalance(this.tokenId, addressObj);3633 }36343635 async getTotalPieces() {3636 return await this.collection.getTokenTotalPieces(this.tokenId);3637 }36383639 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3640 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3641 }36423643 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3644 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3645 }36463647 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3648 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3649 }36503651 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3652 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3653 }36543655 async repartition(signer: TSigner, amount: bigint) {3656 return await this.collection.repartitionToken(signer, this.tokenId, amount);3657 }36583659 async burn(signer: TSigner, amount=1n) {3660 return await this.collection.burnToken(signer, this.tokenId, amount);3661 }36623663 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3664 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3665 }36663667 scheduleAt<T extends UniqueHelper>(3668 executionBlockNumber: number,3669 options: ISchedulerOptions = {},3670 ) {3671 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3672 return new UniqueRFToken(this.tokenId, scheduledCollection);3673 }36743675 scheduleAfter<T extends UniqueHelper>(3676 blocksBeforeExecution: number,3677 options: ISchedulerOptions = {},3678 ) {3679 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3680 return new UniqueRFToken(this.tokenId, scheduledCollection);3681 }36823683 getSudo<T extends UniqueHelper>() {3684 return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3685 }3686}