git.delta.rocks / unique-network / refs/commits / f27580fc65c7

difftreelog

source

tests/src/util/playgrounds/unique.ts116.2 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// SPDX-License-Identifier: Apache-2.034/* eslint-disable @typescript-eslint/no-var-requires */5/* eslint-disable function-call-argument-newline */6/* eslint-disable no-prototype-builtins */78import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';12import {IApiListeners, IBlock, IEvent, IChainProperties, ICollectionCreationOptions, ICollectionLimits, ICollectionPermissions, ICrossAccountId, ICrossAccountIdLower, ILogger, INestingPermissions, IProperty, IStakingInfo, ISchedulerOptions, ISubstrateBalance, IToken, ITokenPropertyPermission, ITransactionResult, IUniqueHelperLog, TApiAllowedListeners, TEthereumAccount, TSigner, TSubstrateAccount, TUniqueNetworks} from './types';1314export class CrossAccountId implements ICrossAccountId {15  Substrate?: TSubstrateAccount;16  Ethereum?: TEthereumAccount;1718  constructor(account: ICrossAccountId) {19    if (account.Substrate) this.Substrate = account.Substrate;20    if (account.Ethereum) this.Ethereum = account.Ethereum;21  }2223  static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {24    switch (domain) {25      case 'Substrate': return new CrossAccountId({Substrate: account.address});26      case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();27    }28  }2930  static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {31    return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});32  }3334  static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {35    return encodeAddress(decodeAddress(address), ss58Format);36  }3738  static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {39    return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});40  }41  42  withNormalizedSubstrate(ss58Format = 42): CrossAccountId {43    if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);44    return this;45  }4647  static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {48    return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));49  }5051  toEthereum(): CrossAccountId {52    if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});53    return this;54  }5556  static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {57    return evmToAddress(address, ss58Format);58  }5960  toSubstrate(ss58Format?: number): CrossAccountId {61    if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});62    return this;63  }64  65  toLowerCase(): CrossAccountId {66    if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();67    if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();68    return this;69  }70}7172const nesting = {73  toChecksumAddress(address: string): string {74    if (typeof address === 'undefined') return '';7576    if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);7778    address = address.toLowerCase().replace(/^0x/i,'');79    const addressHash = keccakAsHex(address).replace(/^0x/i,'');80    const checksumAddress = ['0x'];8182    for (let i = 0; i < address.length; i++) {83      // If ith character is 8 to f then make it uppercase84      if (parseInt(addressHash[i], 16) > 7) {85        checksumAddress.push(address[i].toUpperCase());86      } else {87        checksumAddress.push(address[i]);88      }89    }90    return checksumAddress.join('');91  },92  tokenIdToAddress(collectionId: number, tokenId: number) {93    return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);94  },95};9697class UniqueUtil {98  static transactionStatus = {99    NOT_READY: 'NotReady',100    FAIL: 'Fail',101    SUCCESS: 'Success',102  };103104  static chainLogType = {105    EXTRINSIC: 'extrinsic',106    RPC: 'rpc',107  };108109  static getTokenAccount(token: IToken): CrossAccountId {110    return new CrossAccountId({Ethereum: this.getTokenAddress(token)});111  }112113  static getTokenAddress(token: IToken): string {114    return nesting.tokenIdToAddress(token.collectionId, token.tokenId);115  }116117  static getDefaultLogger(): ILogger {118    return {119      log(msg: any, level = 'INFO') {120        console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));121      },122      level: {123        ERROR: 'ERROR',124        WARNING: 'WARNING',125        INFO: 'INFO',126      },127    };128  }129130  static vec2str(arr: string[] | number[]) {131    return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');132  }133134  static str2vec(string: string) {135    if (typeof string !== 'string') return string;136    return Array.from(string).map(x => x.charCodeAt(0));137  }138139  static fromSeed(seed: string, ss58Format = 42) {140    const keyring = new Keyring({type: 'sr25519', ss58Format});141    return keyring.addFromUri(seed);142  }143144  static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {145    if (creationResult.status !== this.transactionStatus.SUCCESS) {146      throw Error('Unable to create collection!');147    }148149    let collectionId = null;150    creationResult.result.events.forEach(({event: {data, method, section}}) => {151      if ((section === 'common') && (method === 'CollectionCreated')) {152        collectionId = parseInt(data[0].toString(), 10);153      }154    });155156    if (collectionId === null) {157      throw Error('No CollectionCreated event was found!');158    }159160    return collectionId;161  }162163  static extractTokensFromCreationResult(creationResult: ITransactionResult): {164    success: boolean, 165    tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],166  } {167    if (creationResult.status !== this.transactionStatus.SUCCESS) {168      throw Error('Unable to create tokens!');169    }170    let success = false;171    const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];172    creationResult.result.events.forEach(({event: {data, method, section}}) => {173      if (method === 'ExtrinsicSuccess') {174        success = true;175      } else if ((section === 'common') && (method === 'ItemCreated')) {176        tokens.push({177          collectionId: parseInt(data[0].toString(), 10),178          tokenId: parseInt(data[1].toString(), 10),179          owner: data[2].toHuman(),180          amount: data[3].toBigInt(),181        });182      }183    });184    return {success, tokens};185  }186187  static extractTokensFromBurnResult(burnResult: ITransactionResult): {188    success: boolean, 189    tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],190  } {191    if (burnResult.status !== this.transactionStatus.SUCCESS) {192      throw Error('Unable to burn tokens!');193    }194    let success = false;195    const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];196    burnResult.result.events.forEach(({event: {data, method, section}}) => {197      if (method === 'ExtrinsicSuccess') {198        success = true;199      } else if ((section === 'common') && (method === 'ItemDestroyed')) {200        tokens.push({201          collectionId: parseInt(data[0].toString(), 10),202          tokenId: parseInt(data[1].toString(), 10),203          owner: data[2].toHuman(),204          amount: data[3].toBigInt(),205        });206      }207    });208    return {success, tokens};209  }210211  static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {212    let eventId = null;213    events.forEach(({event: {data, method, section}}) => {214      if ((section === expectedSection) && (method === expectedMethod)) {215        eventId = parseInt(data[0].toString(), 10);216      }217    });218219    if (eventId === null) {220      throw Error(`No ${expectedMethod} event was found!`);221    }222    return eventId === collectionId;223  }224225  static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {226    const normalizeAddress = (address: string | ICrossAccountId) => {227      if(typeof address === 'string') return address;228      const obj = {} as any;229      Object.keys(address).forEach(k => {230        obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];231      });232      if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);233      if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();234      return address;235    };236    let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;237    events.forEach(({event: {data, method, section}}) => {238      if ((section === 'common') && (method === 'Transfer')) {239        const hData = (data as any).toJSON();240        transfer = {241          collectionId: hData[0],242          tokenId: hData[1],243          from: normalizeAddress(hData[2]),244          to: normalizeAddress(hData[3]),245          amount: BigInt(hData[4]),246        };247      }248    });249    let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;250    isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);251    isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);252    isSuccess = isSuccess && amount === transfer.amount;253    return isSuccess;254  }255}256257class UniqueEventHelper {258  private static extractIndex(index: any): [number, number] | string {259    if(index.toRawType() === '[u8;2]') return [index[0], index[1]];260    return index.toJSON();261  }262263  private static extractSub(data: any, subTypes: any): {[key: string]: any} {264    let obj: any = {};265    let index = 0;266267    if (data.entries) {268      for(const [key, value] of data.entries()) {269        obj[key] = this.extractData(value, subTypes[index]);270        index++;271      }272    } else obj = data.toJSON();273274    return obj;275  }276  277  private static extractData(data: any, type: any): any {278    if(!type) return data.toHuman();279    if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();280    if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();281    if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);282    return data.toHuman();283  }284285  public static extractEvents(records: ITransactionResult): IEvent[] {286    const parsedEvents: IEvent[] = [];287288    records.result.events.forEach((record) => {289      const {event, phase} = record;290      const types = (event as any).typeDef;291292      const eventData: IEvent = {293        section: event.section.toString(),294        method: event.method.toString(),295        index: this.extractIndex(event.index),296        data: [],297        phase: phase.toJSON(),298      };299300      event.data.forEach((val: any, index: number) => {301        eventData.data.push(this.extractData(val, types[index]));302      });303304      parsedEvents.push(eventData);305    });306307    return parsedEvents;308  }309}310311class ChainHelperBase {312  transactionStatus = UniqueUtil.transactionStatus;313  chainLogType = UniqueUtil.chainLogType;314  util: typeof UniqueUtil;315  eventHelper: typeof UniqueEventHelper;316  logger: ILogger;317  api: ApiPromise | null;318  forcedNetwork: TUniqueNetworks | null;319  network: TUniqueNetworks | null;320  chainLog: IUniqueHelperLog[];321  children: ChainHelperBase[];322323  constructor(logger?: ILogger) {324    this.util = UniqueUtil;325    this.eventHelper = UniqueEventHelper;326    if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();327    this.logger = logger;328    this.api = null;329    this.forcedNetwork = null;330    this.network = null;331    this.chainLog = [];332    this.children = [];333  }334335  getApi(): ApiPromise {336    if(this.api === null) throw Error('API not initialized');337    return this.api;338  }339340  clearChainLog(): void {341    this.chainLog = [];342  }343344  forceNetwork(value: TUniqueNetworks): void {345    this.forcedNetwork = value;346  }347348  async connect(wsEndpoint: string, listeners?: IApiListeners) {349    if (this.api !== null) throw Error('Already connected');350    const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);351    this.api = api;352    this.network = network;353  }354355  async disconnect() {356    for (const child of this.children) {357      child.clearApi();358    }359360    if (this.api === null) return;361    await this.api.disconnect();362    this.clearApi();363  }364365  clearApi() {366    this.api = null;367    this.network = null;368  }369370  static async detectNetwork(api: ApiPromise): Promise<TUniqueNetworks> {371    const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;372    if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;373    return 'opal';374  }375376  static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TUniqueNetworks> {377    const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});378    await api.isReady;379380    const network = await this.detectNetwork(api);381382    await api.disconnect();383384    return network;385  }386387  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TUniqueNetworks | null): Promise<{388    api: ApiPromise;389    network: TUniqueNetworks;390  }> {391    if(typeof network === 'undefined' || network === null) network = 'opal';392    const supportedRPC = {393      opal: {394        unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,395      },396      quartz: {397        unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,398      },399      unique: {400        unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,401      },402    };403    if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);404    const rpc = supportedRPC[network];405406    // TODO: investigate how to replace rpc in runtime407    // api._rpcCore.addUserInterfaces(rpc);408409    const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});410411    await api.isReadyOrError;412413    if (typeof listeners === 'undefined') listeners = {};414    for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {415      if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;416      api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);417    }418419    return {api, network};420  }421422  getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {423    const {events, status} = data;424    if (status.isReady) {425      return this.transactionStatus.NOT_READY;426    }427    if (status.isBroadcast) {428      return this.transactionStatus.NOT_READY;429    }430    if (status.isInBlock || status.isFinalized) {431      const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');432      if (errors.length > 0) {433        return this.transactionStatus.FAIL;434      }435      if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {436        return this.transactionStatus.SUCCESS;437      }438    }439440    return this.transactionStatus.FAIL;441  }442443  signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {444    const sign = (callback: any) => {445      if(options !== null) return transaction.signAndSend(sender, options, callback);446      return transaction.signAndSend(sender, callback);447    };448    // eslint-disable-next-line no-async-promise-executor449    return new Promise(async (resolve, reject) => {450      try {451        const unsub = await sign((result: any) => {452          const status = this.getTransactionStatus(result);453454          if (status === this.transactionStatus.SUCCESS) {455            this.logger.log(`${label} successful`);456            unsub();457            resolve({result, status});458          } else if (status === this.transactionStatus.FAIL) {459            let moduleError = null;460461            if (result.hasOwnProperty('dispatchError')) {462              const dispatchError = result['dispatchError'];463464              if (dispatchError) {465                if (dispatchError.isModule) {466                  const modErr = dispatchError.asModule;467                  const errorMeta = dispatchError.registry.findMetaError(modErr);468469                  moduleError = `${errorMeta.section}.${errorMeta.name}`;470                } else {471                  moduleError = dispatchError.toHuman();472                }473              } else {474                this.logger.log(result, this.logger.level.ERROR);475              }476            }477478            this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);479            unsub();480            reject({status, moduleError, result});481          }482        });483      } catch (e) {484        this.logger.log(e, this.logger.level.ERROR);485        reject(e);486      }487    });488  }489490  constructApiCall(apiCall: string, params: any[]) {491    if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);492    let call = this.getApi() as any;493    for(const part of apiCall.slice(4).split('.')) {494      call = call[part];495    }496    return call(...params);497  }498499  async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {500    if(this.api === null) throw Error('API not initialized');501    if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);502503    const startTime = (new Date()).getTime();504    let result: ITransactionResult;505    let events: IEvent[] = [];506    try {507      result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;508      events = this.eventHelper.extractEvents(result);509    }510    catch(e) {511      if(!(e as object).hasOwnProperty('status')) throw e;512      result = e as ITransactionResult;513    }514515    const endTime = (new Date()).getTime();516517    const log = {518      executedAt: endTime,519      executionTime: endTime - startTime,520      type: this.chainLogType.EXTRINSIC,521      status: result.status,522      call: extrinsic,523      signer: this.getSignerAddress(sender),524      params,525    } as IUniqueHelperLog;526527    if(result.status !== this.transactionStatus.SUCCESS) {528      if (result.moduleError) log.moduleError = result.moduleError;529      else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;530    }531    if(events.length > 0) log.events = events;532533    this.chainLog.push(log);534535    if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {536      if (result.moduleError) throw Error(`${result.moduleError}`);537      else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));538    }539    return result;540  }541542  async callRpc(rpc: string, params?: any[]) {543    if(typeof params === 'undefined') params = [];544    if(this.api === null) throw Error('API not initialized');545    if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);546547    const startTime = (new Date()).getTime();548    let result;549    let error = null;550    const log = {551      type: this.chainLogType.RPC,552      call: rpc,553      params,554    } as IUniqueHelperLog;555556    try {557      result = await this.constructApiCall(rpc, params);558    }559    catch(e) {560      error = e;561    }562563    const endTime = (new Date()).getTime();564565    log.executedAt = endTime;566    log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';567    log.executionTime = endTime - startTime;568569    this.chainLog.push(log);570571    if(error !== null) throw error;572573    return result;574  }575576  getSignerAddress(signer: IKeyringPair | string): string {577    if(typeof signer === 'string') return signer;578    return signer.address;579  }580581  fetchAllPalletNames(): string[] {582    if(this.api === null) throw Error('API not initialized');583    return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());584  }585586  fetchMissingPalletNames(requiredPallets: string[]): string[] {587    const palletNames = this.fetchAllPalletNames();588    return requiredPallets.filter(p => !palletNames.includes(p));589  }590}591592593class HelperGroup {594  helper: UniqueHelper;595596  constructor(uniqueHelper: UniqueHelper) {597    this.helper = uniqueHelper;598  }599}600601602class CollectionGroup extends HelperGroup {603  /**604 * Get number of blocks when sponsored transaction is available.605 *606 * @param collectionId ID of collection607 * @param tokenId ID of token608 * @param addressObj address for which the sponsorship is checked609 * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});610 * @returns number of blocks or null if sponsorship hasn't been set611 */612  async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {613    return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();614  }615616  /**617   * Get the number of created collections.618   *619   * @returns number of created collections620   */621  async getTotalCount(): Promise<number> {622    return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();623  }624625  /**626   * Get information about the collection with additional data,627   * including the number of tokens it contains, its administrators,628   * the normalized address of the collection's owner, and decoded name and description.629   *630   * @param collectionId ID of collection631   * @example await getData(2)632   * @returns collection information object633   */634  async getData(collectionId: number): Promise<{635    id: number;636    name: string;637    description: string;638    tokensCount: number;639    admins: CrossAccountId[];640    normalizedOwner: TSubstrateAccount;641    raw: any642  } | null> {643    const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);644    const humanCollection = collection.toHuman(), collectionData = {645      id: collectionId, name: null, description: null, tokensCount: 0, admins: [],646      raw: humanCollection,647    } as any, jsonCollection = collection.toJSON();648    if (humanCollection === null) return null;649    collectionData.raw.limits = jsonCollection.limits;650    collectionData.raw.permissions = jsonCollection.permissions;651    collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);652    for (const key of ['name', 'description']) {653      collectionData[key] = this.helper.util.vec2str(humanCollection[key]);654    }655656    collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))657      ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)658      : 0;659    collectionData.admins = await this.getAdmins(collectionId);660661    return collectionData;662  }663664  /**665   * Get the addresses of the collection's administrators, optionally normalized.666   *667   * @param collectionId ID of collection668   * @param normalize whether to normalize the addresses to the default ss58 format669   * @example await getAdmins(1)670   * @returns array of administrators671   */672  async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {673    const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();674675    return normalize676      ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())677      : admins;678  }679680  /**681   * Get the addresses added to the collection allow-list, optionally normalized.682   * @param collectionId ID of collection683   * @param normalize whether to normalize the addresses to the default ss58 format684   * @example await getAllowList(1)685   * @returns array of allow-listed addresses686   */687  async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {688    const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();689    return normalize690      ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())691      : allowListed;692  }693694  /**695   * Get the effective limits of the collection instead of null for default values696   *697   * @param collectionId ID of collection698   * @example await getEffectiveLimits(2)699   * @returns object of collection limits700   */701  async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {702    return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();703  }704705  /**706   * Burns the collection if the signer has sufficient permissions and collection is empty.707   *708   * @param signer keyring of signer709   * @param collectionId ID of collection710   * @example await helper.collection.burn(aliceKeyring, 3);711   * @returns ```true``` if extrinsic success, otherwise ```false```712   */713  async burn(signer: TSigner, collectionId: number): Promise<boolean> {714    const result = await this.helper.executeExtrinsic(715      signer,716      'api.tx.unique.destroyCollection', [collectionId],717      true,718    );719720    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');721  }722723  /**724   * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.725   *726   * @param signer keyring of signer727   * @param collectionId ID of collection728   * @param sponsorAddress Sponsor substrate address729   * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")730   * @returns ```true``` if extrinsic success, otherwise ```false```731   */732  async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {733    const result = await this.helper.executeExtrinsic(734      signer,735      'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],736      true,737    );738739    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet');740  }741742  /**743   * Confirms consent to sponsor the collection on behalf of the signer.744   *745   * @param signer keyring of signer746   * @param collectionId ID of collection747   * @example confirmSponsorship(aliceKeyring, 10)748   * @returns ```true``` if extrinsic success, otherwise ```false```749   */750  async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {751    const result = await this.helper.executeExtrinsic(752      signer,753      'api.tx.unique.confirmSponsorship', [collectionId],754      true,755    );756757    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed');758  }759760  /**761   * Removes the sponsor of a collection, regardless if it consented or not.762   *763   * @param signer keyring of signer764   * @param collectionId ID of collection765   * @example removeSponsor(aliceKeyring, 10)766   * @returns ```true``` if extrinsic success, otherwise ```false```767   */768  async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {769    const result = await this.helper.executeExtrinsic(770      signer,771      'api.tx.unique.removeCollectionSponsor', [collectionId],772      true,773    );774775    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorRemoved');776  }777778  /**779   * Sets the limits of the collection. At least one limit must be specified for a correct call.780   *781   * @param signer keyring of signer782   * @param collectionId ID of collection783   * @param limits collection limits object784   * @example785   * await setLimits(786   *   aliceKeyring,787   *   10,788   *   {789   *     sponsorTransferTimeout: 0,790   *     ownerCanDestroy: false791   *   }792   * )793   * @returns ```true``` if extrinsic success, otherwise ```false```794   */795  async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {796    const result = await this.helper.executeExtrinsic(797      signer,798      'api.tx.unique.setCollectionLimits', [collectionId, limits],799      true,800    );801802    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet');803  }804805  /**806   * Changes the owner of the collection to the new Substrate address.807   *808   * @param signer keyring of signer809   * @param collectionId ID of collection810   * @param ownerAddress substrate address of new owner811   * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")812   * @returns ```true``` if extrinsic success, otherwise ```false```813   */814  async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {815    const result = await this.helper.executeExtrinsic(816      signer,817      'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],818      true,819    );820821    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged');822  }823824  /**825   * Adds a collection administrator.826   *827   * @param signer keyring of signer828   * @param collectionId ID of collection829   * @param adminAddressObj Administrator address (substrate or ethereum)830   * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})831   * @returns ```true``` if extrinsic success, otherwise ```false```832   */833  async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {834    const result = await this.helper.executeExtrinsic(835      signer,836      'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],837      true,838    );839840    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded');841  }842843  /**844   * Removes a collection administrator.845   *846   * @param signer keyring of signer847   * @param collectionId ID of collection848   * @param adminAddressObj Administrator address (substrate or ethereum)849   * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})850   * @returns ```true``` if extrinsic success, otherwise ```false```851   */852  async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {853    const result = await this.helper.executeExtrinsic(854      signer,855      'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],856      true,857    );858859    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved');860  }861862  /**863   * Check if user is in allow list.864   * 865   * @param collectionId ID of collection866   * @param user Account to check867   * @example await getAdmins(1)868   * @returns is user in allow list869   */870  async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {871    return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();872  }873874  /**875   * Adds an address to allow list876   * @param signer keyring of signer877   * @param collectionId ID of collection878   * @param addressObj address to add to the allow list879   * @returns ```true``` if extrinsic success, otherwise ```false```880   */881  async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {882    const result = await this.helper.executeExtrinsic(883      signer,884      'api.tx.unique.addToAllowList', [collectionId, addressObj],885      true,886    );887888    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressAdded');889  }890891  /**892   * Removes an address from allow list893   *894   * @param signer keyring of signer895   * @param collectionId ID of collection896   * @param addressObj address to remove from the allow list897   * @returns ```true``` if extrinsic success, otherwise ```false```898   */899  async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {900    const result = await this.helper.executeExtrinsic(901      signer,902      'api.tx.unique.removeFromAllowList', [collectionId, addressObj],903      true,904    );905906    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved');907  }908909  /**910   * Sets onchain permissions for selected collection.911   *912   * @param signer keyring of signer913   * @param collectionId ID of collection914   * @param permissions collection permissions object915   * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});916   * @returns ```true``` if extrinsic success, otherwise ```false```917   */918  async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {919    const result = await this.helper.executeExtrinsic(920      signer,921      'api.tx.unique.setCollectionPermissions', [collectionId, permissions],922      true,923    );924925    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet');926  }927928  /**929   * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.930   *931   * @param signer keyring of signer932   * @param collectionId ID of collection933   * @param permissions nesting permissions object934   * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});935   * @returns ```true``` if extrinsic success, otherwise ```false```936   */937  async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {938    return await this.setPermissions(signer, collectionId, {nesting: permissions});939  }940941  /**942   * Disables nesting for selected collection.943   *944   * @param signer keyring of signer945   * @param collectionId ID of collection946   * @example disableNesting(aliceKeyring, 10);947   * @returns ```true``` if extrinsic success, otherwise ```false```948   */949  async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {950    return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});951  }952953  /**954   * Sets onchain properties to the collection.955   *956   * @param signer keyring of signer957   * @param collectionId ID of collection958   * @param properties array of property objects959   * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);960   * @returns ```true``` if extrinsic success, otherwise ```false```961   */962  async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {963    const result = await this.helper.executeExtrinsic(964      signer,965      'api.tx.unique.setCollectionProperties', [collectionId, properties],966      true,967    );968969    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');970  }971972  /**973   * Get collection properties.974   * 975   * @param collectionId ID of collection976   * @param propertyKeys optionally filter the returned properties to only these keys977   * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);978   * @returns array of key-value pairs979   */980  async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {981    return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();982  }983984  async getCollectionOptions(collectionId: number) {985    return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();986  }987988  /**989   * Deletes onchain properties from the collection.990   *991   * @param signer keyring of signer992   * @param collectionId ID of collection993   * @param propertyKeys array of property keys to delete994   * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);995   * @returns ```true``` if extrinsic success, otherwise ```false```996   */997  async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {998    const result = await this.helper.executeExtrinsic(999      signer,1000      'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1001      true,1002    );10031004    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1005  }10061007  /**1008   * Changes the owner of the token.1009   *1010   * @param signer keyring of signer1011   * @param collectionId ID of collection1012   * @param tokenId ID of token1013   * @param addressObj address of a new owner1014   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1015   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1016   * @returns true if the token success, otherwise false1017   */1018  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1019    const result = await this.helper.executeExtrinsic(1020      signer,1021      'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1022      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1023    );10241025    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1026  }10271028  /**1029   *1030   * Change ownership of a token(s) on behalf of the owner.1031   *1032   * @param signer keyring of signer1033   * @param collectionId ID of collection1034   * @param tokenId ID of token1035   * @param fromAddressObj address on behalf of which the token will be sent1036   * @param toAddressObj new token owner1037   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1038   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})1039   * @returns true if the token success, otherwise false1040   */1041  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1042    const result = await this.helper.executeExtrinsic(1043      signer,1044      'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1045      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1046    );1047    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1048  }10491050  /**1051   *1052   * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.1053   *1054   * @param signer keyring of signer1055   * @param collectionId ID of collection1056   * @param tokenId ID of token1057   * @param amount amount of tokens to be burned. For NFT must be set to 1n1058   * @example burnToken(aliceKeyring, 10, 5);1059   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1060   */1061  async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1062    const burnResult = await this.helper.executeExtrinsic(1063      signer,1064      'api.tx.unique.burnItem', [collectionId, tokenId, amount],1065      true, // `Unable to burn token for ${label}`,1066    );1067    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1068    if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1069    return burnedTokens.success;1070  }10711072  /**1073   * Destroys a concrete instance of NFT on behalf of the owner1074   *1075   * @param signer keyring of signer1076   * @param collectionId ID of collection1077   * @param tokenId ID of token1078   * @param fromAddressObj address on behalf of which the token will be burnt1079   * @param amount amount of tokens to be burned. For NFT must be set to 1n1080   * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})1081   * @returns ```true``` if extrinsic success, otherwise ```false```1082   */1083  async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1084    const burnResult = await this.helper.executeExtrinsic(1085      signer,1086      'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1087      true, // `Unable to burn token from for ${label}`,1088    );1089    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1090    return burnedTokens.success && burnedTokens.tokens.length > 0;1091  }10921093  /**1094   * Set, change, or remove approved address to transfer the ownership of the NFT.1095   *1096   * @param signer keyring of signer1097   * @param collectionId ID of collection1098   * @param tokenId ID of token1099   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens1100   * @param amount amount of token to be approved. For NFT must be set to 1n1101   * @returns ```true``` if extrinsic success, otherwise ```false```1102   */1103  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1104    const approveResult = await this.helper.executeExtrinsic(1105      signer,1106      'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1107      true, // `Unable to approve token for ${label}`,1108    );11091110    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1111  }11121113  /**1114   * Get the amount of token pieces approved to transfer or burn. Normally 0.1115   *1116   * @param collectionId ID of collection1117   * @param tokenId ID of token1118   * @param toAccountObj address which is approved to use token pieces1119   * @param fromAccountObj address which may have allowed the use of its owned tokens1120   * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})1121   * @returns number of approved to transfer pieces1122   */1123  async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1124    return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1125  }11261127  /**1128   * Get the last created token ID in a collection1129   *1130   * @param collectionId ID of collection1131   * @example getLastTokenId(10);1132   * @returns id of the last created token1133   */1134  async getLastTokenId(collectionId: number): Promise<number> {1135    return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1136  }11371138  /**1139   * Check if token exists1140   *1141   * @param collectionId ID of collection1142   * @param tokenId ID of token1143   * @example doesTokenExist(10, 20);1144   * @returns true if the token exists, otherwise false1145   */1146  async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1147    return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1148  }1149}11501151class NFTnRFT extends CollectionGroup {1152  /**1153   * Get tokens owned by account1154   *1155   * @param collectionId ID of collection1156   * @param addressObj tokens owner1157   * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})1158   * @returns array of token ids owned by account1159   */1160  async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1161    return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1162  }11631164  /**1165   * Get token data1166   *1167   * @param collectionId ID of collection1168   * @param tokenId ID of token1169   * @param propertyKeys optionally filter the token properties to only these keys1170   * @param blockHashAt optionally query the data at some block with this hash1171   * @example getToken(10, 5);1172   * @returns human readable token data1173   */1174  async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1175    properties: IProperty[];1176    owner: CrossAccountId;1177    normalizedOwner: CrossAccountId;1178  }| null> {1179    let tokenData;1180    if(typeof blockHashAt === 'undefined') {1181      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1182    }1183    else {1184      if(propertyKeys.length == 0) {1185        const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1186        if(!collection) return null;1187        propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1188      }1189      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1190    }1191    tokenData = tokenData.toHuman();1192    if (tokenData === null || tokenData.owner === null) return null;1193    const owner = {} as any;1194    for (const key of Object.keys(tokenData.owner)) {1195      owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate' 1196        ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key]) 1197        : tokenData.owner[key];1198    }1199    tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1200    return tokenData;1201  }12021203  /**1204   * Set permissions to change token properties1205   *1206   * @param signer keyring of signer1207   * @param collectionId ID of collection1208   * @param permissions permissions to change a property by the collection admin or token owner1209   * @example setTokenPropertyPermissions(1210   *   aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]1211   * )1212   * @returns true if extrinsic success otherwise false1213   */1214  async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1215    const result = await this.helper.executeExtrinsic(1216      signer,1217      'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1218      true,1219    );12201221    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1222  }12231224  /**1225   * Get token property permissions.1226   * 1227   * @param collectionId ID of collection1228   * @param propertyKeys optionally filter the returned property permissions to only these keys1229   * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);1230   * @returns array of key-permission pairs1231   */1232  async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1233    return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1234  }12351236  /**1237   * Set token properties1238   *1239   * @param signer keyring of signer1240   * @param collectionId ID of collection1241   * @param tokenId ID of token1242   * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection1243   * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])1244   * @returns ```true``` if extrinsic success, otherwise ```false```1245   */1246  async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1247    const result = await this.helper.executeExtrinsic(1248      signer,1249      'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1250      true,1251    );12521253    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1254  }12551256  /**1257   * Get properties, metadata assigned to a token.1258   * 1259   * @param collectionId ID of collection1260   * @param tokenId ID of token1261   * @param propertyKeys optionally filter the returned properties to only these keys1262   * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);1263   * @returns array of key-value pairs1264   */1265  async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1266    return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1267  }12681269  /**1270   * Delete the provided properties of a token1271   * @param signer keyring of signer1272   * @param collectionId ID of collection1273   * @param tokenId ID of token1274   * @param propertyKeys property keys to be deleted1275   * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])1276   * @returns ```true``` if extrinsic success, otherwise ```false```1277   */1278  async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1279    const result = await this.helper.executeExtrinsic(1280      signer,1281      'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1282      true,1283    );12841285    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1286  }12871288  /**1289   * Mint new collection1290   *1291   * @param signer keyring of signer1292   * @param collectionOptions basic collection options and properties1293   * @param mode NFT or RFT type of a collection1294   * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")1295   * @returns object of the created collection1296   */1297  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1298    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1299    collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1300    for (const key of ['name', 'description', 'tokenPrefix']) {1301      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);1302    }1303    const creationResult = await this.helper.executeExtrinsic(1304      signer,1305      'api.tx.unique.createCollectionEx', [collectionOptions],1306      true, // errorLabel,1307    );1308    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1309  }13101311  getCollectionObject(_collectionId: number): any {1312    return null;1313  }13141315  getTokenObject(_collectionId: number, _tokenId: number): any {1316    return null;1317  }1318}131913201321class NFTGroup extends NFTnRFT {1322  /**1323   * Get collection object1324   * @param collectionId ID of collection1325   * @example getCollectionObject(2);1326   * @returns instance of UniqueNFTCollection1327   */1328  getCollectionObject(collectionId: number): UniqueNFTCollection {1329    return new UniqueNFTCollection(collectionId, this.helper);1330  }13311332  /**1333   * Get token object1334   * @param collectionId ID of collection1335   * @param tokenId ID of token1336   * @example getTokenObject(10, 5);1337   * @returns instance of UniqueNFTToken1338   */1339  getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1340    return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1341  }13421343  /**1344   * Get token's owner1345   * @param collectionId ID of collection1346   * @param tokenId ID of token1347   * @param blockHashAt optionally query the data at the block with this hash1348   * @example getTokenOwner(10, 5);1349   * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}1350   */1351  async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1352    let owner;1353    if (typeof blockHashAt === 'undefined') {1354      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1355    } else {1356      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1357    }1358    return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1359  }13601361  /**1362   * Is token approved to transfer1363   * @param collectionId ID of collection1364   * @param tokenId ID of token1365   * @param toAccountObj address to be approved1366   * @returns ```true``` if extrinsic success, otherwise ```false```1367   */1368  async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1369    return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1370  }13711372  /**1373   * Changes the owner of the token.1374   *1375   * @param signer keyring of signer1376   * @param collectionId ID of collection1377   * @param tokenId ID of token1378   * @param addressObj address of a new owner1379   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1380   * @returns ```true``` if extrinsic success, otherwise ```false```1381   */1382  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1383    return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1384  }13851386  /**1387   *1388   * Change ownership of a NFT on behalf of the owner.1389   *1390   * @param signer keyring of signer1391   * @param collectionId ID of collection1392   * @param tokenId ID of token1393   * @param fromAddressObj address on behalf of which the token will be sent1394   * @param toAddressObj new token owner1395   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})1396   * @returns ```true``` if extrinsic success, otherwise ```false```1397   */1398  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1399    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1400  }14011402  /**1403   * Recursively find the address that owns the token1404   * @param collectionId ID of collection1405   * @param tokenId ID of token1406   * @param blockHashAt1407   * @example getTokenTopmostOwner(10, 5);1408   * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}1409   */1410  async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1411    let owner;1412    if (typeof blockHashAt === 'undefined') {1413      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1414    } else {1415      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1416    }14171418    if (owner === null) return null;14191420    return owner.toHuman();1421  }14221423  /**1424   * Get tokens nested in the provided token1425   * @param collectionId ID of collection1426   * @param tokenId ID of token1427   * @param blockHashAt optionally query the data at the block with this hash1428   * @example getTokenChildren(10, 5);1429   * @returns tokens whose depth of nesting is <= 51430   */1431  async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1432    let children;1433    if(typeof blockHashAt === 'undefined') {1434      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1435    } else {1436      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1437    }14381439    return children.toJSON().map((x: any) => {1440      return {collectionId: x.collection, tokenId: x.token};1441    });1442  }14431444  /**1445   * Nest one token into another1446   * @param signer keyring of signer1447   * @param tokenObj token to be nested1448   * @param rootTokenObj token to be parent1449   * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});1450   * @returns ```true``` if extrinsic success, otherwise ```false```1451   */1452  async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1453    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1454    const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1455    if(!result) {1456      throw Error('Unable to nest token!');1457    }1458    return result;1459  }14601461  /**1462   * Remove token from nested state1463   * @param signer keyring of signer1464   * @param tokenObj token to unnest1465   * @param rootTokenObj parent of a token1466   * @param toAddressObj address of a new token owner1467   * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});1468   * @returns ```true``` if extrinsic success, otherwise ```false```1469   */1470  async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1471    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1472    const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1473    if(!result) {1474      throw Error('Unable to unnest token!');1475    }1476    return result;1477  }14781479  /**1480   * Mint new collection1481   * @param signer keyring of signer1482   * @param collectionOptions Collection options1483   * @example1484   * mintCollection(aliceKeyring, {1485   *   name: 'New',1486   *   description: 'New collection',1487   *   tokenPrefix: 'NEW',1488   * })1489   * @returns object of the created collection1490   */1491  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1492    return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1493  }14941495  /**1496   * Mint new token1497   * @param signer keyring of signer1498   * @param data token data1499   * @returns created token object1500   */1501  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1502    const creationResult = await this.helper.executeExtrinsic(1503      signer,1504      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1505        nft: {1506          properties: data.properties,1507        },1508      }],1509      true,1510    );1511    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1512    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1513    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1514    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1515  }15161517  /**1518   * Mint multiple NFT tokens1519   * @param signer keyring of signer1520   * @param collectionId ID of collection1521   * @param tokens array of tokens with owner and properties1522   * @example1523   * mintMultipleTokens(aliceKeyring, 10, [{1524   *     owner: {Substrate: "5DyN4Y92vZCjv38fg..."},1525   *     properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],1526   *   },{1527   *     owner: {Ethereum: "0x9F0583DbB855d..."},1528   *     properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],1529   * }]);1530   * @returns ```true``` if extrinsic success, otherwise ```false```1531   */1532  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1533    const creationResult = await this.helper.executeExtrinsic(1534      signer,1535      'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1536      true,1537    );1538    const collection = this.getCollectionObject(collectionId);1539    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1540  }15411542  /**1543   * Mint multiple NFT tokens with one owner1544   * @param signer keyring of signer1545   * @param collectionId ID of collection1546   * @param owner tokens owner1547   * @param tokens array of tokens with owner and properties1548   * @example1549   * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{1550   *   properties: [{1551   *   key: "gender",1552   *   value: "female",1553   *  },{1554   *   key: "age",1555   *   value: "33",1556   *  }],1557   * }]);1558   * @returns array of newly created tokens1559   */1560  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1561    const rawTokens = [];1562    for (const token of tokens) {1563      const raw = {NFT: {properties: token.properties}};1564      rawTokens.push(raw);1565    }1566    const creationResult = await this.helper.executeExtrinsic(1567      signer,1568      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1569      true,1570    );1571    const collection = this.getCollectionObject(collectionId);1572    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1573  }15741575  /**1576   * Set, change, or remove approved address to transfer the ownership of the NFT.1577   *1578   * @param signer keyring of signer1579   * @param collectionId ID of collection1580   * @param tokenId ID of token1581   * @param toAddressObj address to approve1582   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1583   * @returns ```true``` if extrinsic success, otherwise ```false```1584   */1585  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1586    return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1587  }1588}158915901591class RFTGroup extends NFTnRFT {1592  /**1593   * Get collection object1594   * @param collectionId ID of collection1595   * @example getCollectionObject(2);1596   * @returns instance of UniqueRFTCollection1597   */1598  getCollectionObject(collectionId: number): UniqueRFTCollection {1599    return new UniqueRFTCollection(collectionId, this.helper);1600  }16011602  /**1603   * Get token object1604   * @param collectionId ID of collection1605   * @param tokenId ID of token1606   * @example getTokenObject(10, 5);1607   * @returns instance of UniqueNFTToken1608   */1609  getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1610    return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1611  }16121613  /**1614   * Get top 10 token owners with the largest number of pieces1615   * @param collectionId ID of collection1616   * @param tokenId ID of token1617   * @example getTokenTop10Owners(10, 5);1618   * @returns array of top 10 owners1619   */1620  async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1621    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1622  }16231624  /**1625   * Get number of pieces owned by address1626   * @param collectionId ID of collection1627   * @param tokenId ID of token1628   * @param addressObj address token owner1629   * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});1630   * @returns number of pieces ownerd by address1631   */1632  async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1633    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1634  }16351636  /**1637   * Transfer pieces of token to another address1638   * @param signer keyring of signer1639   * @param collectionId ID of collection1640   * @param tokenId ID of token1641   * @param addressObj address of a new owner1642   * @param amount number of pieces to be transfered1643   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)1644   * @returns ```true``` if extrinsic success, otherwise ```false```1645   */1646  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1647    return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1648  }16491650  /**1651   * Change ownership of some pieces of RFT on behalf of the owner.1652   * @param signer keyring of signer1653   * @param collectionId ID of collection1654   * @param tokenId ID of token1655   * @param fromAddressObj address on behalf of which the token will be sent1656   * @param toAddressObj new token owner1657   * @param amount number of pieces to be transfered1658   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)1659   * @returns ```true``` if extrinsic success, otherwise ```false```1660   */1661  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1662    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1663  }16641665  /**1666   * Mint new collection1667   * @param signer keyring of signer1668   * @param collectionOptions Collection options1669   * @example1670   * mintCollection(aliceKeyring, {1671   *   name: 'New',1672   *   description: 'New collection',1673   *   tokenPrefix: 'NEW',1674   * })1675   * @returns object of the created collection1676   */1677  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1678    return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1679  }16801681  /**1682   * Mint new token1683   * @param signer keyring of signer1684   * @param data token data1685   * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});1686   * @returns created token object1687   */1688  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1689    const creationResult = await this.helper.executeExtrinsic(1690      signer,1691      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1692        refungible: {1693          pieces: data.pieces,1694          properties: data.properties,1695        },1696      }],1697      true,1698    );1699    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1700    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1701    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1702    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1703  }17041705  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1706    throw Error('Not implemented');1707    const creationResult = await this.helper.executeExtrinsic(1708      signer,1709      'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1710      true, // `Unable to mint RFT tokens for ${label}`,1711    );1712    const collection = this.getCollectionObject(collectionId);1713    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1714  }17151716  /**1717   * Mint multiple RFT tokens with one owner1718   * @param signer keyring of signer1719   * @param collectionId ID of collection1720   * @param owner tokens owner1721   * @param tokens array of tokens with properties and pieces1722   * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);1723   * @returns array of newly created RFT tokens1724   */1725  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1726    const rawTokens = [];1727    for (const token of tokens) {1728      const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1729      rawTokens.push(raw);1730    }1731    const creationResult = await this.helper.executeExtrinsic(1732      signer,1733      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1734      true,1735    );1736    const collection = this.getCollectionObject(collectionId);1737    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1738  }17391740  /**1741   * Destroys a concrete instance of RFT.1742   * @param signer keyring of signer1743   * @param collectionId ID of collection1744   * @param tokenId ID of token1745   * @param amount number of pieces to be burnt1746   * @example burnToken(aliceKeyring, 10, 5);1747   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1748   */1749  async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1750    return await super.burnToken(signer, collectionId, tokenId, amount);1751  }17521753  /**1754   * Destroys a concrete instance of RFT on behalf of the owner.1755   * @param signer keyring of signer1756   * @param collectionId ID of collection1757   * @param tokenId ID of token1758   * @param fromAddressObj address on behalf of which the token will be burnt1759   * @param amount number of pieces to be burnt1760   * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)1761   * @returns ```true``` if extrinsic success, otherwise ```false```1762   */1763  async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1764    return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1765  }17661767  /**1768   * Set, change, or remove approved address to transfer the ownership of the RFT.1769   *1770   * @param signer keyring of signer1771   * @param collectionId ID of collection1772   * @param tokenId ID of token1773   * @param toAddressObj address to approve1774   * @param amount number of pieces to be approved1775   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);1776   * @returns true if the token success, otherwise false1777   */1778  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1779    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1780  }17811782  /**1783   * Get total number of pieces1784   * @param collectionId ID of collection1785   * @param tokenId ID of token1786   * @example getTokenTotalPieces(10, 5);1787   * @returns number of pieces1788   */1789  async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1790    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1791  }17921793  /**1794   * Change number of token pieces. Signer must be the owner of all token pieces.1795   * @param signer keyring of signer1796   * @param collectionId ID of collection1797   * @param tokenId ID of token1798   * @param amount new number of pieces1799   * @example repartitionToken(aliceKeyring, 10, 5, 12345n);1800   * @returns true if the repartion was success, otherwise false1801   */1802  async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1803    const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1804    const repartitionResult = await this.helper.executeExtrinsic(1805      signer,1806      'api.tx.unique.repartition', [collectionId, tokenId, amount],1807      true,1808    );1809    if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1810    return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1811  }1812}181318141815class FTGroup extends CollectionGroup {1816  /**1817   * Get collection object1818   * @param collectionId ID of collection1819   * @example getCollectionObject(2);1820   * @returns instance of UniqueFTCollection1821   */1822  getCollectionObject(collectionId: number): UniqueFTCollection {1823    return new UniqueFTCollection(collectionId, this.helper);1824  }18251826  /**1827   * Mint new fungible collection1828   * @param signer keyring of signer1829   * @param collectionOptions Collection options1830   * @param decimalPoints number of token decimals1831   * @example1832   * mintCollection(aliceKeyring, {1833   *   name: 'New',1834   *   description: 'New collection',1835   *   tokenPrefix: 'NEW',1836   * }, 18)1837   * @returns newly created fungible collection1838   */1839  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1840    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1841    if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1842    collectionOptions.mode = {fungible: decimalPoints};1843    for (const key of ['name', 'description', 'tokenPrefix']) {1844      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);1845    }1846    const creationResult = await this.helper.executeExtrinsic(1847      signer,1848      'api.tx.unique.createCollectionEx', [collectionOptions],1849      true,1850    );1851    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1852  }18531854  /**1855   * Mint tokens1856   * @param signer keyring of signer1857   * @param collectionId ID of collection1858   * @param owner address owner of new tokens1859   * @param amount amount of tokens to be meanted1860   * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);1861   * @returns ```true``` if extrinsic success, otherwise ```false```1862   */1863  async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {1864    const creationResult = await this.helper.executeExtrinsic(1865      signer,1866      'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {1867        fungible: {1868          value: amount,1869        },1870      }],1871      true, // `Unable to mint fungible tokens for ${label}`,1872    );1873    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1874  }18751876  /**1877   * Mint multiple Fungible tokens with one owner1878   * @param signer keyring of signer1879   * @param collectionId ID of collection1880   * @param owner tokens owner1881   * @param tokens array of tokens with properties and pieces1882   * @returns ```true``` if extrinsic success, otherwise ```false```1883   */1884  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {1885    const rawTokens = [];1886    for (const token of tokens) {1887      const raw = {Fungible: {Value: token.value}};1888      rawTokens.push(raw);1889    }1890    const creationResult = await this.helper.executeExtrinsic(1891      signer,1892      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1893      true,1894    );1895    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1896  }18971898  /**1899   * Get the top 10 owners with the largest balance for the Fungible collection1900   * @param collectionId ID of collection1901   * @example getTop10Owners(10);1902   * @returns array of ```ICrossAccountId```1903   */1904  async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {1905    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1906  }19071908  /**1909   * Get account balance1910   * @param collectionId ID of collection1911   * @param addressObj address of owner1912   * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})1913   * @returns amount of fungible tokens owned by address1914   */1915  async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {1916    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();1917  }19181919  /**1920   * Transfer tokens to address1921   * @param signer keyring of signer1922   * @param collectionId ID of collection1923   * @param toAddressObj address recipient1924   * @param amount amount of tokens to be sent1925   * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);1926   * @returns ```true``` if extrinsic success, otherwise ```false```1927   */1928  async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {1929    return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);1930  }19311932  /**1933   * Transfer some tokens on behalf of the owner.1934   * @param signer keyring of signer1935   * @param collectionId ID of collection1936   * @param fromAddressObj address on behalf of which tokens will be sent1937   * @param toAddressObj address where token to be sent1938   * @param amount number of tokens to be sent1939   * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);1940   * @returns ```true``` if extrinsic success, otherwise ```false```1941   */1942  async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {1943    return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);1944  }19451946  /**1947   * Destroy some amount of tokens1948   * @param signer keyring of signer1949   * @param collectionId ID of collection1950   * @param amount amount of tokens to be destroyed1951   * @example burnTokens(aliceKeyring, 10, 1000n);1952   * @returns ```true``` if extrinsic success, otherwise ```false```1953   */1954  async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {1955    return await super.burnToken(signer, collectionId, 0, amount);1956  }19571958  /**1959   * Burn some tokens on behalf of the owner.1960   * @param signer keyring of signer1961   * @param collectionId ID of collection1962   * @param fromAddressObj address on behalf of which tokens will be burnt1963   * @param amount amount of tokens to be burnt1964   * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);1965   * @returns ```true``` if extrinsic success, otherwise ```false```1966   */1967  async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1968    return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);1969  }19701971  /**1972   * Get total collection supply1973   * @param collectionId1974   * @returns1975   */1976  async getTotalPieces(collectionId: number): Promise<bigint> {1977    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();1978  }19791980  /**1981   * Set, change, or remove approved address to transfer tokens.1982   *1983   * @param signer keyring of signer1984   * @param collectionId ID of collection1985   * @param toAddressObj address to be approved1986   * @param amount amount of tokens to be approved1987   * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)1988   * @returns ```true``` if extrinsic success, otherwise ```false```1989   */1990  async approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {1991    return super.approveToken(signer, collectionId, 0, toAddressObj, amount);1992  }19931994  /**1995   * Get amount of fungible tokens approved to transfer1996   * @param collectionId ID of collection1997   * @param fromAddressObj owner of tokens1998   * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner1999   * @returns number of tokens approved for the transfer2000   */2001  async getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2002    return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2003  }2004}200520062007class ChainGroup extends HelperGroup {2008  /**2009   * Get system properties of a chain2010   * @example getChainProperties();2011   * @returns ss58Format, token decimals, and token symbol2012   */2013  getChainProperties(): IChainProperties {2014    const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2015    return {2016      ss58Format: properties.ss58Format.toJSON(),2017      tokenDecimals: properties.tokenDecimals.toJSON(),2018      tokenSymbol: properties.tokenSymbol.toJSON(),2019    };2020  }20212022  /**2023   * Get chain header2024   * @example getLatestBlockNumber();2025   * @returns the number of the last block2026   */2027  async getLatestBlockNumber(): Promise<number> {2028    return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2029  }20302031  /**2032   * Get block hash by block number2033   * @param blockNumber number of block2034   * @example getBlockHashByNumber(12345);2035   * @returns hash of a block2036   */2037  async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2038    const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2039    if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2040    return blockHash;2041  }20422043  // TODO add docs2044  async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2045    const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2046    if (!blockHash) return null;2047    return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2048  }20492050  /**2051   * Get account nonce2052   * @param address substrate address2053   * @example getNonce("5GrwvaEF5zXb26Fz...");2054   * @returns number, account's nonce2055   */2056  async getNonce(address: TSubstrateAccount): Promise<number> {2057    return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2058  }2059}206020612062class BalanceGroup extends HelperGroup {2063  getCollectionCreationPrice(): bigint {2064    return 2n * this.helper.balance.getOneTokenNominal();2065  }2066  /**2067   * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2068   * @example getOneTokenNominal()2069   * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.2070   */2071  getOneTokenNominal(): bigint {2072    const chainProperties = this.helper.chain.getChainProperties();2073    return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2074  }20752076  /**2077   * Get substrate address balance2078   * @param address substrate address2079   * @example getSubstrate("5GrwvaEF5zXb26Fz...")2080   * @returns amount of tokens on address2081   */2082  async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2083    return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2084  }20852086  /**2087   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2088   * @param address substrate address2089   * @returns2090   */2091  async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2092    const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2093    return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2094  }20952096  /**2097   * Get ethereum address balance2098   * @param address ethereum address2099   * @example getEthereum("0x9F0583DbB855d...")2100   * @returns amount of tokens on address2101   */2102  async getEthereum(address: TEthereumAccount): Promise<bigint> {2103    return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2104  }21052106  /**2107   * Transfer tokens to substrate address2108   * @param signer keyring of signer2109   * @param address substrate address of a recipient2110   * @param amount amount of tokens to be transfered2111   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2112   * @returns ```true``` if extrinsic success, otherwise ```false```2113   */2114  async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2115    const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true/*, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`*/);21162117    let transfer = {from: null, to: null, amount: 0n} as any;2118    result.result.events.forEach(({event: {data, method, section}}) => {2119      if ((section === 'balances') && (method === 'Transfer')) {2120        transfer = {2121          from: this.helper.address.normalizeSubstrate(data[0]),2122          to: this.helper.address.normalizeSubstrate(data[1]),2123          amount: BigInt(data[2]),2124        };2125      }2126    });2127    const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from 2128      && this.helper.address.normalizeSubstrate(address) === transfer.to 2129      && BigInt(amount) === transfer.amount;2130    return isSuccess;2131  }2132}213321342135class AddressGroup extends HelperGroup {2136  /**2137   * Normalizes the address to the specified ss58 format, by default ```42```.2138   * @param address substrate address2139   * @param ss58Format format for address conversion, by default ```42```2140   * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY2141   * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation2142   */2143  normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2144    return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2145  }21462147  /**2148   * Get address in the connected chain format2149   * @param address substrate address2150   * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network2151   * @returns address in chain format2152   */2153  normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2154    return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2155  }21562157  /**2158   * Get substrate mirror of an ethereum address2159   * @param ethAddress ethereum address2160   * @param toChainFormat false for normalized account2161   * @example ethToSubstrate('0x9F0583DbB855d...')2162   * @returns substrate mirror of a provided ethereum address2163   */2164  ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2165    return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2166  }21672168  /**2169   * Get ethereum mirror of a substrate address2170   * @param subAddress substrate account2171   * @example substrateToEth("5DnSF6RRjwteE3BrC...")2172   * @returns ethereum mirror of a provided substrate address2173   */2174  substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2175    return CrossAccountId.translateSubToEth(subAddress);2176  }2177}21782179class StakingGroup extends HelperGroup {2180  /**2181   * Stake tokens for App Promotion2182   * @param signer keyring of signer2183   * @param amountToStake amount of tokens to stake2184   * @param label extra label for log2185   * @returns2186   */2187  async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2188    if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2189    const _stakeResult = await this.helper.executeExtrinsic(2190      signer, 'api.tx.appPromotion.stake',2191      [amountToStake], true,2192    );2193    // TODO extract info from stakeResult2194    return true;2195  }21962197  /**2198   * Unstake tokens for App Promotion2199   * @param signer keyring of signer2200   * @param amountToUnstake amount of tokens to unstake2201   * @param label extra label for log2202   * @returns block number where balances will be unlocked2203   */2204  async unstake(signer: TSigner, label?: string): Promise<number> {2205    if(typeof label === 'undefined') label = `${signer.address}`;2206    const _unstakeResult = await this.helper.executeExtrinsic(2207      signer, 'api.tx.appPromotion.unstake',2208      [], true,2209    );2210    // TODO extract block number fron events2211    return 1;2212  }22132214  /**2215   * Get total staked amount for address2216   * @param address substrate or ethereum address2217   * @returns total staked amount2218   */2219  async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2220    if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2221    return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2222  }22232224  /**2225   * Get total staked per block2226   * @param address substrate or ethereum address2227   * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block2228   */2229  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2230    const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2231    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2232      return { 2233        block: block.toBigInt(),2234        amount: amount.toBigInt(),2235      };2236    });2237  }22382239  /**2240   * Get total pending unstake amount for address2241   * @param address substrate or ethereum address2242   * @returns total pending unstake amount2243   */2244  async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2245    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2246  }22472248  /**2249   * Get pending unstake amount per block for address2250   * @param address substrate or ethereum address2251   * @returns array of pending stakes. `block` – the number of the block in which the unstake was made. `amount` - the number of tokens unstaked in the block2252   */2253  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2254    const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2255    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2256      return {2257        block: block.toBigInt(),2258        amount: amount.toBigInt(),2259      };2260    });2261    return result;2262  }2263}22642265class SchedulerGroup extends HelperGroup {2266  constructor(helper: UniqueHelper) {2267    super(helper);2268  }22692270  async cancelScheduled(signer: TSigner, scheduledId: string) {2271    return this.helper.executeExtrinsic(2272      signer,2273      'api.tx.scheduler.cancelNamed',2274      [scheduledId],2275      true,2276    );2277  }22782279  async changePriority(signer: TSigner, scheduledId: string, priority: number) {2280    return this.helper.executeExtrinsic(2281      signer,2282      'api.tx.scheduler.changeNamedPriority',2283      [scheduledId, priority],2284      true,2285    );2286  }22872288  scheduleAt<T extends UniqueHelper>(2289    scheduledId: string,2290    executionBlockNumber: number,2291    options: ISchedulerOptions = {},2292  ) {2293    return this.schedule<T>('scheduleNamed', scheduledId, executionBlockNumber, options);2294  }22952296  scheduleAfter<T extends UniqueHelper>(2297    scheduledId: string,2298    blocksBeforeExecution: number,2299    options: ISchedulerOptions = {},2300  ) {2301    return this.schedule<T>('scheduleNamedAfter', scheduledId, blocksBeforeExecution, options);2302  }23032304  schedule<T extends UniqueHelper>(2305    scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter',2306    scheduledId: string,2307    blocksNum: number,2308    options: ISchedulerOptions = {},2309  ) {2310    // eslint-disable-next-line @typescript-eslint/naming-convention2311    const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2312    return this.helper.clone(ScheduledHelperType, {2313      scheduleFn,2314      scheduledId,2315      blocksNum,2316      options,2317    }) as T;2318  }2319}23202321export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;23222323export class UniqueHelper extends ChainHelperBase {2324  helperBase: any;23252326  chain: ChainGroup;2327  balance: BalanceGroup;2328  address: AddressGroup;2329  collection: CollectionGroup;2330  nft: NFTGroup;2331  rft: RFTGroup;2332  ft: FTGroup;2333  staking: StakingGroup;2334  scheduler: SchedulerGroup;23352336  constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2337    super(logger);23382339    this.helperBase = options.helperBase ?? UniqueHelper;23402341    this.chain = new ChainGroup(this);2342    this.balance = new BalanceGroup(this);2343    this.address = new AddressGroup(this);2344    this.collection = new CollectionGroup(this);2345    this.nft = new NFTGroup(this);2346    this.rft = new RFTGroup(this);2347    this.ft = new FTGroup(this);2348    this.staking = new StakingGroup(this);2349    this.scheduler = new SchedulerGroup(this);2350  }23512352  clone(helperCls: UniqueHelperConstructor, options: {[key: string]: any} = {}) {2353    Object.setPrototypeOf(helperCls.prototype, this);2354    const newHelper = new helperCls(this.logger, options);23552356    newHelper.api = this.api;2357    newHelper.network = this.network;2358    newHelper.forceNetwork = this.forceNetwork;23592360    this.children.push(newHelper);23612362    return newHelper;2363  }23642365  getSudo<T extends UniqueHelper>() {2366    // eslint-disable-next-line @typescript-eslint/naming-convention2367    const SudoHelperType = SudoUniqueHelper(this.helperBase);2368    return this.clone(SudoHelperType) as T;2369  }2370}23712372// eslint-disable-next-line @typescript-eslint/naming-convention2373function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2374  return class extends Base {2375    scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter';2376    scheduledId: string;2377    blocksNum: number;2378    options: ISchedulerOptions;23792380    constructor(...args: any[]) {2381      const logger = args[0] as ILogger;2382      const options = args[1] as {2383        scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter',2384        scheduledId: string,2385        blocksNum: number,2386        options: ISchedulerOptions2387      };23882389      super(logger);23902391      this.scheduleFn = options.scheduleFn;2392      this.scheduledId = options.scheduledId;2393      this.blocksNum = options.blocksNum;2394      this.options = options.options;2395    }23962397    executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {2398      const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);2399      const extrinsic = 'api.tx.scheduler.' +  this.scheduleFn;24002401      return super.executeExtrinsic(2402        sender,2403        extrinsic,2404        [2405          this.scheduledId,2406          this.blocksNum,2407          this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,2408          this.options.priority ?? null,2409          {Value: scheduledTx},2410        ],2411        expectSuccess,2412      );2413    }2414  };2415}24162417// eslint-disable-next-line @typescript-eslint/naming-convention2418function SudoUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2419  return class extends Base {2420    constructor(...args: any[]) {2421      super(...args);2422    }24232424    executeExtrinsic (2425      sender: IKeyringPair,2426      extrinsic: string,2427      params: any[],2428      expectSuccess?: boolean,2429    ): Promise<ITransactionResult> {2430      const call = this.constructApiCall(extrinsic, params);24312432      return super.executeExtrinsic(2433        sender,2434        'api.tx.sudo.sudo',2435        [call],2436        expectSuccess,2437      );2438    }2439  };2440}24412442export class UniqueBaseCollection {2443  helper: UniqueHelper;2444  collectionId: number;24452446  constructor(collectionId: number, uniqueHelper: UniqueHelper) {2447    this.collectionId = collectionId;2448    this.helper = uniqueHelper;2449  }24502451  async getData() {2452    return await this.helper.collection.getData(this.collectionId);2453  }24542455  async getLastTokenId() {2456    return await this.helper.collection.getLastTokenId(this.collectionId);2457  }24582459  async doesTokenExist(tokenId: number) {2460    return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);2461  }24622463  async getAdmins() {2464    return await this.helper.collection.getAdmins(this.collectionId);2465  }24662467  async getAllowList() {2468    return await this.helper.collection.getAllowList(this.collectionId);2469  }24702471  async getEffectiveLimits() {2472    return await this.helper.collection.getEffectiveLimits(this.collectionId);2473  }24742475  async getProperties(propertyKeys?: string[] | null) {2476    return await this.helper.collection.getProperties(this.collectionId, propertyKeys);2477  }24782479  async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {2480    return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);2481  }24822483  async getOptions() {2484    return await this.helper.collection.getCollectionOptions(this.collectionId);2485  }24862487  async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {2488    return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);2489  }24902491  async confirmSponsorship(signer: TSigner) {2492    return await this.helper.collection.confirmSponsorship(signer, this.collectionId);2493  }24942495  async removeSponsor(signer: TSigner) {2496    return await this.helper.collection.removeSponsor(signer, this.collectionId);2497  }24982499  async setLimits(signer: TSigner, limits: ICollectionLimits) {2500    return await this.helper.collection.setLimits(signer, this.collectionId, limits);2501  }25022503  async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {2504    return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);2505  }25062507  async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {2508    return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);2509  }25102511  async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {2512    return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);2513  }25142515  async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {2516    return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);2517  }25182519  async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {2520    return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);2521  }25222523  async setProperties(signer: TSigner, properties: IProperty[]) {2524    return await this.helper.collection.setProperties(signer, this.collectionId, properties);2525  }25262527  async deleteProperties(signer: TSigner, propertyKeys: string[]) {2528    return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);2529  }25302531  async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {2532    return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);2533  }25342535  async enableNesting(signer: TSigner, permissions: INestingPermissions) {2536    return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);2537  }25382539  async disableNesting(signer: TSigner) {2540    return await this.helper.collection.disableNesting(signer, this.collectionId);2541  }25422543  async burn(signer: TSigner) {2544    return await this.helper.collection.burn(signer, this.collectionId);2545  }25462547  scheduleAt<T extends UniqueHelper>(2548    scheduledId: string,2549    executionBlockNumber: number,2550    options: ISchedulerOptions = {},2551  ) {2552    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);2553    return new UniqueBaseCollection(this.collectionId, scheduledHelper);2554  }25552556  scheduleAfter<T extends UniqueHelper>(2557    scheduledId: string,2558    blocksBeforeExecution: number,2559    options: ISchedulerOptions = {},2560  ) {2561    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);2562    return new UniqueBaseCollection(this.collectionId, scheduledHelper);2563  }25642565  getSudo<T extends UniqueHelper>() {2566    return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());2567  }2568}256925702571export class UniqueNFTCollection extends UniqueBaseCollection {2572  getTokenObject(tokenId: number) {2573    return new UniqueNFToken(tokenId, this);2574  }25752576  async getTokensByAddress(addressObj: ICrossAccountId) {2577    return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);2578  }25792580  async getToken(tokenId: number, blockHashAt?: string) {2581    return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);2582  }25832584  async getTokenOwner(tokenId: number, blockHashAt?: string) {2585    return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);2586  }25872588  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {2589    return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);2590  }25912592  async getTokenChildren(tokenId: number, blockHashAt?: string) {2593    return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);2594  }25952596  async getPropertyPermissions(propertyKeys: string[] | null = null) {2597    return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);2598  }25992600  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {2601    return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);2602  }26032604  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {2605    return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);2606  }26072608  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2609    return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);2610  }26112612  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {2613    return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);2614  }26152616  async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {2617    return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);2618  }26192620  async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {2621    return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});2622  }26232624  async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {2625    return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);2626  }26272628  async burnToken(signer: TSigner, tokenId: number) {2629    return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);2630  }26312632  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {2633    return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);2634  }26352636  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {2637    return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);2638  }26392640  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {2641    return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);2642  }26432644  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {2645    return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);2646  }26472648  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {2649    return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);2650  }26512652  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {2653    return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);2654  }26552656  scheduleAt<T extends UniqueHelper>(2657    scheduledId: string,2658    executionBlockNumber: number,2659    options: ISchedulerOptions = {},2660  ) {2661    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);2662    return new UniqueNFTCollection(this.collectionId, scheduledHelper);2663  }26642665  scheduleAfter<T extends UniqueHelper>(2666    scheduledId: string,2667    blocksBeforeExecution: number,2668    options: ISchedulerOptions = {},2669  ) {2670    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);2671    return new UniqueNFTCollection(this.collectionId, scheduledHelper);2672  }26732674  getSudo<T extends UniqueHelper>() {2675    return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());2676  }2677}267826792680export class UniqueRFTCollection extends UniqueBaseCollection {2681  getTokenObject(tokenId: number) {2682    return new UniqueRFToken(tokenId, this);2683  }26842685  async getToken(tokenId: number, blockHashAt?: string) {2686    return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);2687  }26882689  async getTokensByAddress(addressObj: ICrossAccountId) {2690    return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);2691  }26922693  async getTop10TokenOwners(tokenId: number) {2694    return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);2695  }26962697  async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {2698    return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);2699  }27002701  async getTokenTotalPieces(tokenId: number) {2702    return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);2703  }27042705  async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2706    return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);2707  }27082709  async getPropertyPermissions(propertyKeys: string[] | null = null) {2710    return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);2711  }27122713  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {2714    return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);2715  }27162717  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {2718    return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);2719  }27202721  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2722    return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);2723  }27242725  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {2726    return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);2727  }27282729  async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {2730    return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);2731  }27322733  async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {2734    return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});2735  }27362737  async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {2738    return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);2739  }27402741  async burnToken(signer: TSigner, tokenId: number, amount=1n) {2742    return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);2743  }27442745  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId,  amount=1n) {2746    return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);2747  }27482749  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {2750    return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);2751  }27522753  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {2754    return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);2755  }27562757  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {2758    return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);2759  }27602761  scheduleAt<T extends UniqueHelper>(2762    scheduledId: string,2763    executionBlockNumber: number,2764    options: ISchedulerOptions = {},2765  ) {2766    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);2767    return new UniqueRFTCollection(this.collectionId, scheduledHelper);2768  }27692770  scheduleAfter<T extends UniqueHelper>(2771    scheduledId: string,2772    blocksBeforeExecution: number,2773    options: ISchedulerOptions = {},2774  ) {2775    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);2776    return new UniqueRFTCollection(this.collectionId, scheduledHelper);2777  }27782779  getSudo<T extends UniqueHelper>() {2780    return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());2781  }2782}278327842785export class UniqueFTCollection extends UniqueBaseCollection {2786  async getBalance(addressObj: ICrossAccountId) {2787    return await this.helper.ft.getBalance(this.collectionId, addressObj);2788  }27892790  async getTotalPieces() {2791    return await this.helper.ft.getTotalPieces(this.collectionId);2792  }27932794  async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2795    return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);2796  }27972798  async getTop10Owners() {2799    return await this.helper.ft.getTop10Owners(this.collectionId);2800  }28012802  async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {2803    return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);2804  }28052806  async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {2807    return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);2808  }28092810  async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {2811    return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);2812  }28132814  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2815    return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);2816  }28172818  async burnTokens(signer: TSigner, amount=1n) {2819    return await this.helper.ft.burnTokens(signer, this.collectionId, amount);2820  }28212822  async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {2823    return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);2824  }28252826  async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {2827    return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);2828  }28292830  scheduleAt<T extends UniqueHelper>(2831    scheduledId: string,2832    executionBlockNumber: number,2833    options: ISchedulerOptions = {},2834  ) {2835    const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);2836    return new UniqueFTCollection(this.collectionId, scheduledHelper);2837  }28382839  scheduleAfter<T extends UniqueHelper>(2840    scheduledId: string,2841    blocksBeforeExecution: number,2842    options: ISchedulerOptions = {},2843  ) {2844    const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);2845    return new UniqueFTCollection(this.collectionId, scheduledHelper);2846  }28472848  getSudo<T extends UniqueHelper>() {2849    return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());2850  }2851}285228532854export class UniqueBaseToken {2855  collection: UniqueNFTCollection | UniqueRFTCollection;2856  collectionId: number;2857  tokenId: number;28582859  constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {2860    this.collection = collection;2861    this.collectionId = collection.collectionId;2862    this.tokenId = tokenId;2863  }28642865  async getNextSponsored(addressObj: ICrossAccountId) {2866    return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);2867  }28682869  async getProperties(propertyKeys?: string[] | null) {2870    return await this.collection.getTokenProperties(this.tokenId, propertyKeys);2871  }28722873  async setProperties(signer: TSigner, properties: IProperty[]) {2874    return await this.collection.setTokenProperties(signer, this.tokenId, properties);2875  }28762877  async deleteProperties(signer: TSigner, propertyKeys: string[]) {2878    return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);2879  }28802881  async doesExist() {2882    return await this.collection.doesTokenExist(this.tokenId);2883  }28842885  nestingAccount() {2886    return this.collection.helper.util.getTokenAccount(this);2887  }28882889  scheduleAt<T extends UniqueHelper>(2890    scheduledId: string,2891    executionBlockNumber: number,2892    options: ISchedulerOptions = {},2893  ) {2894    const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);2895    return new UniqueBaseToken(this.tokenId, scheduledCollection);2896  }28972898  scheduleAfter<T extends UniqueHelper>(2899    scheduledId: string,2900    blocksBeforeExecution: number,2901    options: ISchedulerOptions = {},2902  ) {2903    const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);2904    return new UniqueBaseToken(this.tokenId, scheduledCollection);2905  }29062907  getSudo<T extends UniqueHelper>() {2908    return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());2909  }2910}291129122913export class UniqueNFToken extends UniqueBaseToken {2914  collection: UniqueNFTCollection;29152916  constructor(tokenId: number, collection: UniqueNFTCollection) {2917    super(tokenId, collection);2918    this.collection = collection;2919  }29202921  async getData(blockHashAt?: string) {2922    return await this.collection.getToken(this.tokenId, blockHashAt);2923  }29242925  async getOwner(blockHashAt?: string) {2926    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);2927  }29282929  async getTopmostOwner(blockHashAt?: string) {2930    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);2931  }29322933  async getChildren(blockHashAt?: string) {2934    return await this.collection.getTokenChildren(this.tokenId, blockHashAt);2935  }29362937  async nest(signer: TSigner, toTokenObj: IToken) {2938    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);2939  }29402941  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {2942    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);2943  }29442945  async transfer(signer: TSigner, addressObj: ICrossAccountId) {2946    return await this.collection.transferToken(signer, this.tokenId, addressObj);2947  }29482949  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2950    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);2951  }29522953  async approve(signer: TSigner, toAddressObj: ICrossAccountId) {2954    return await this.collection.approveToken(signer, this.tokenId, toAddressObj);2955  }29562957  async isApproved(toAddressObj: ICrossAccountId) {2958    return await this.collection.isTokenApproved(this.tokenId, toAddressObj);2959  }29602961  async burn(signer: TSigner) {2962    return await this.collection.burnToken(signer, this.tokenId);2963  }29642965  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {2966    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);2967  }29682969  scheduleAt<T extends UniqueHelper>(2970    scheduledId: string,2971    executionBlockNumber: number,2972    options: ISchedulerOptions = {},2973  ) {2974    const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);2975    return new UniqueNFToken(this.tokenId, scheduledCollection);2976  }29772978  scheduleAfter<T extends UniqueHelper>(2979    scheduledId: string,2980    blocksBeforeExecution: number,2981    options: ISchedulerOptions = {},2982  ) {2983    const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);2984    return new UniqueNFToken(this.tokenId, scheduledCollection);2985  }29862987  getSudo<T extends UniqueHelper>() {2988    return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());2989  }2990}29912992export class UniqueRFToken extends UniqueBaseToken {2993  collection: UniqueRFTCollection;29942995  constructor(tokenId: number, collection: UniqueRFTCollection) {2996    super(tokenId, collection);2997    this.collection = collection;2998  }29993000  async getData(blockHashAt?: string) {3001    return await this.collection.getToken(this.tokenId, blockHashAt);3002  }30033004  async getTop10Owners() {3005    return await this.collection.getTop10TokenOwners(this.tokenId);3006  }30073008  async getBalance(addressObj: ICrossAccountId) {3009    return await this.collection.getTokenBalance(this.tokenId, addressObj);3010  }30113012  async getTotalPieces() {3013    return await this.collection.getTokenTotalPieces(this.tokenId);3014  }30153016  async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3017    return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3018  }30193020  async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3021    return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3022  }30233024  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3025    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3026  }30273028  async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3029    return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3030  }30313032  async repartition(signer: TSigner, amount: bigint) {3033    return await this.collection.repartitionToken(signer, this.tokenId, amount);3034  }30353036  async burn(signer: TSigner, amount=1n) {3037    return await this.collection.burnToken(signer, this.tokenId, amount);3038  }30393040  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3041    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3042  }30433044  scheduleAt<T extends UniqueHelper>(3045    scheduledId: string,3046    executionBlockNumber: number,3047    options: ISchedulerOptions = {},3048  ) {3049    const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3050    return new UniqueRFToken(this.tokenId, scheduledCollection);3051  }30523053  scheduleAfter<T extends UniqueHelper>(3054    scheduledId: string,3055    blocksBeforeExecution: number,3056    options: ISchedulerOptions = {},3057  ) {3058    const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3059    return new UniqueRFToken(this.tokenId, scheduledCollection);3060  }30613062  getSudo<T extends UniqueHelper>() {3063    return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3064  }3065}