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

difftreelog

source

tests/src/util/playgrounds/unique.ts105.8 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, 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[];321322  constructor(logger?: ILogger) {323    this.util = UniqueUtil;324    this.eventHelper = UniqueEventHelper;325    if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();326    this.logger = logger;327    this.api = null;328    this.forcedNetwork = null;329    this.network = null;330    this.chainLog = [];331  }332333  clearChainLog(): void {334    this.chainLog = [];335  }336337  forceNetwork(value: TUniqueNetworks): void {338    this.forcedNetwork = value;339  }340341  async connect(wsEndpoint: string, listeners?: IApiListeners) {342    if (this.api !== null) throw Error('Already connected');343    const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);344    this.api = api;345    this.network = network;346  }347348  async disconnect() {349    if (this.api === null) return;350    await this.api.disconnect();351    this.api = null;352    this.network = null;353  }354355  static async detectNetwork(api: ApiPromise): Promise<TUniqueNetworks> {356    const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;357    if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;358    return 'opal';359  }360361  static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TUniqueNetworks> {362    const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});363    await api.isReady;364365    const network = await this.detectNetwork(api);366367    await api.disconnect();368369    return network;370  }371372  static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TUniqueNetworks | null): Promise<{373    api: ApiPromise;374    network: TUniqueNetworks;375  }> {376    if(typeof network === 'undefined' || network === null) network = 'opal';377    const supportedRPC = {378      opal: {379        unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,380      },381      quartz: {382        unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,383      },384      unique: {385        unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,386      },387    };388    if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);389    const rpc = supportedRPC[network];390391    // TODO: investigate how to replace rpc in runtime392    // api._rpcCore.addUserInterfaces(rpc);393394    const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});395396    await api.isReadyOrError;397398    if (typeof listeners === 'undefined') listeners = {};399    for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {400      if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;401      api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);402    }403404    return {api, network};405  }406407  getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {408    const {events, status} = data;409    if (status.isReady) {410      return this.transactionStatus.NOT_READY;411    }412    if (status.isBroadcast) {413      return this.transactionStatus.NOT_READY;414    }415    if (status.isInBlock || status.isFinalized) {416      const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');417      if (errors.length > 0) {418        return this.transactionStatus.FAIL;419      }420      if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {421        return this.transactionStatus.SUCCESS;422      }423    }424425    return this.transactionStatus.FAIL;426  }427428  signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {429    const sign = (callback: any) => {430      if(options !== null) return transaction.signAndSend(sender, options, callback);431      return transaction.signAndSend(sender, callback);432    };433    // eslint-disable-next-line no-async-promise-executor434    return new Promise(async (resolve, reject) => {435      try {436        const unsub = await sign((result: any) => {437          const status = this.getTransactionStatus(result);438439          if (status === this.transactionStatus.SUCCESS) {440            this.logger.log(`${label} successful`);441            unsub();442            resolve({result, status});443          } else if (status === this.transactionStatus.FAIL) {444            let moduleError = null;445446            if (result.hasOwnProperty('dispatchError')) {447              const dispatchError = result['dispatchError'];448449              if (dispatchError) {450                if (dispatchError.isModule) {451                  const modErr = dispatchError.asModule;452                  const errorMeta = dispatchError.registry.findMetaError(modErr);453454                  moduleError = `${errorMeta.section}.${errorMeta.name}`;455                } else {456                  moduleError = dispatchError.toHuman();457                }458              } else {459                this.logger.log(result, this.logger.level.ERROR);460              }461            }462463            this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);464            unsub();465            reject({status, moduleError, result});466          }467        });468      } catch (e) {469        this.logger.log(e, this.logger.level.ERROR);470        reject(e);471      }472    });473  }474475  constructApiCall(apiCall: string, params: any[]) {476    if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);477    let call = this.api as any;478    for(const part of apiCall.slice(4).split('.')) {479      call = call[part];480    }481    return call(...params);482  }483484  async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null/*, failureMessage='expected success'*/) {485    if(this.api === null) throw Error('API not initialized');486    if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);487488    const startTime = (new Date()).getTime();489    let result: ITransactionResult;490    let events: IEvent[] = [];491    try {492      result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;493      events = this.eventHelper.extractEvents(result);494    }495    catch(e) {496      if(!(e as object).hasOwnProperty('status')) throw e;497      result = e as ITransactionResult;498    }499500    const endTime = (new Date()).getTime();501502    const log = {503      executedAt: endTime,504      executionTime: endTime - startTime,505      type: this.chainLogType.EXTRINSIC,506      status: result.status,507      call: extrinsic,508      signer: this.getSignerAddress(sender),509      params,510    } as IUniqueHelperLog;511512    if(result.status !== this.transactionStatus.SUCCESS && result.moduleError) log.moduleError = result.moduleError;513    if(events.length > 0) log.events = events;514515    this.chainLog.push(log);516517    if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) throw Error(`${result.moduleError}`);518    return result;519  }520521  async callRpc(rpc: string, params?: any[]) {522    if(typeof params === 'undefined') params = [];523    if(this.api === null) throw Error('API not initialized');524    if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);525526    const startTime = (new Date()).getTime();527    let result;528    let error = null;529    const log = {530      type: this.chainLogType.RPC,531      call: rpc,532      params,533    } as IUniqueHelperLog;534535    try {536      result = await this.constructApiCall(rpc, params);537    }538    catch(e) {539      error = e;540    }541542    const endTime = (new Date()).getTime();543544    log.executedAt = endTime;545    log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';546    log.executionTime = endTime - startTime;547548    this.chainLog.push(log);549550    if(error !== null) throw error;551552    return result;553  }554555  getSignerAddress(signer: IKeyringPair | string): string {556    if(typeof signer === 'string') return signer;557    return signer.address;558  }559560  fetchAllPalletNames(): string[] {561    if(this.api === null) throw Error('API not initialized');562    return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());563  }564565  fetchMissingPalletNames(requiredPallets: string[]): string[] {566    const palletNames = this.fetchAllPalletNames();567    return requiredPallets.filter(p => !palletNames.includes(p));568  }569}570571572class HelperGroup {573  helper: UniqueHelper;574575  constructor(uniqueHelper: UniqueHelper) {576    this.helper = uniqueHelper;577  }578}579580581class CollectionGroup extends HelperGroup {582  /**583 * Get number of blocks when sponsored transaction is available.584 *585 * @param collectionId ID of collection586 * @param tokenId ID of token587 * @param addressObj address for which the sponsorship is checked588 * @example await getTokenNextSponsored(1, 2, {Substrate: '5DfhbVfww7ThF8q6f3...'});589 * @returns number of blocks or null if sponsorship hasn't been set590 */591  async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {592    return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();593  }594595  /**596   * Get the number of created collections.597   *598   * @returns number of created collections599   */600  async getTotalCount(): Promise<number> {601    return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();602  }603604  /**605   * Get information about the collection with additional data,606   * including the number of tokens it contains, its administrators,607   * the normalized address of the collection's owner, and decoded name and description.608   *609   * @param collectionId ID of collection610   * @example await getData(2)611   * @returns collection information object612   */613  async getData(collectionId: number): Promise<{614    id: number;615    name: string;616    description: string;617    tokensCount: number;618    admins: CrossAccountId[];619    normalizedOwner: TSubstrateAccount;620    raw: any621  } | null> {622    const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);623    const humanCollection = collection.toHuman(), collectionData = {624      id: collectionId, name: null, description: null, tokensCount: 0, admins: [],625      raw: humanCollection,626    } as any, jsonCollection = collection.toJSON();627    if (humanCollection === null) return null;628    collectionData.raw.limits = jsonCollection.limits;629    collectionData.raw.permissions = jsonCollection.permissions;630    collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);631    for (const key of ['name', 'description']) {632      collectionData[key] = this.helper.util.vec2str(humanCollection[key]);633    }634635    collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))636      ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)637      : 0;638    collectionData.admins = await this.getAdmins(collectionId);639640    return collectionData;641  }642643  /**644   * Get the addresses of the collection's administrators, optionally normalized.645   *646   * @param collectionId ID of collection647   * @param normalize whether to normalize the addresses to the default ss58 format648   * @example await getAdmins(1)649   * @returns array of administrators650   */651  async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {652    const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();653654    return normalize655      ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())656      : admins;657  }658659  /**660   * Get the addresses added to the collection allow-list, optionally normalized.661   * @param collectionId ID of collection662   * @param normalize whether to normalize the addresses to the default ss58 format663   * @example await getAllowList(1)664   * @returns array of allow-listed addresses665   */666  async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {667    const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();668    return normalize669      ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())670      : allowListed;671  }672673  /**674   * Get the effective limits of the collection instead of null for default values675   *676   * @param collectionId ID of collection677   * @example await getEffectiveLimits(2)678   * @returns object of collection limits679   */680  async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {681    return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();682  }683684  /**685   * Burns the collection if the signer has sufficient permissions and collection is empty.686   *687   * @param signer keyring of signer688   * @param collectionId ID of collection689   * @example await helper.collection.burn(aliceKeyring, 3);690   * @returns ```true``` if extrinsic success, otherwise ```false```691   */692  async burn(signer: TSigner, collectionId: number): Promise<boolean> {693    const result = await this.helper.executeExtrinsic(694      signer,695      'api.tx.unique.destroyCollection', [collectionId],696      true,697    );698699    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');700  }701702  /**703   * Sets the sponsor for the collection (Requires the Substrate address). Needs confirmation by the sponsor.704   *705   * @param signer keyring of signer706   * @param collectionId ID of collection707   * @param sponsorAddress Sponsor substrate address708   * @example setSponsor(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")709   * @returns ```true``` if extrinsic success, otherwise ```false```710   */711  async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {712    const result = await this.helper.executeExtrinsic(713      signer,714      'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],715      true,716    );717718    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet');719  }720721  /**722   * Confirms consent to sponsor the collection on behalf of the signer.723   *724   * @param signer keyring of signer725   * @param collectionId ID of collection726   * @example confirmSponsorship(aliceKeyring, 10)727   * @returns ```true``` if extrinsic success, otherwise ```false```728   */729  async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {730    const result = await this.helper.executeExtrinsic(731      signer,732      'api.tx.unique.confirmSponsorship', [collectionId],733      true,734    );735736    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed');737  }738739  /**740   * Removes the sponsor of a collection, regardless if it consented or not.741   *742   * @param signer keyring of signer743   * @param collectionId ID of collection744   * @example removeSponsor(aliceKeyring, 10)745   * @returns ```true``` if extrinsic success, otherwise ```false```746   */747  async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {748    const result = await this.helper.executeExtrinsic(749      signer,750      'api.tx.unique.removeCollectionSponsor', [collectionId],751      true,752    );753754    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorRemoved');755  }756757  /**758   * Sets the limits of the collection. At least one limit must be specified for a correct call.759   *760   * @param signer keyring of signer761   * @param collectionId ID of collection762   * @param limits collection limits object763   * @example764   * await setLimits(765   *   aliceKeyring,766   *   10,767   *   {768   *     sponsorTransferTimeout: 0,769   *     ownerCanDestroy: false770   *   }771   * )772   * @returns ```true``` if extrinsic success, otherwise ```false```773   */774  async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {775    const result = await this.helper.executeExtrinsic(776      signer,777      'api.tx.unique.setCollectionLimits', [collectionId, limits],778      true,779    );780781    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet');782  }783784  /**785   * Changes the owner of the collection to the new Substrate address.786   *787   * @param signer keyring of signer788   * @param collectionId ID of collection789   * @param ownerAddress substrate address of new owner790   * @example changeOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...")791   * @returns ```true``` if extrinsic success, otherwise ```false```792   */793  async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {794    const result = await this.helper.executeExtrinsic(795      signer,796      'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],797      true,798    );799800    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged');801  }802803  /**804   * Adds a collection administrator.805   *806   * @param signer keyring of signer807   * @param collectionId ID of collection808   * @param adminAddressObj Administrator address (substrate or ethereum)809   * @example addAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})810   * @returns ```true``` if extrinsic success, otherwise ```false```811   */812  async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {813    const result = await this.helper.executeExtrinsic(814      signer,815      'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],816      true,817    );818819    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded');820  }821822  /**823   * Removes a collection administrator.824   *825   * @param signer keyring of signer826   * @param collectionId ID of collection827   * @param adminAddressObj Administrator address (substrate or ethereum)828   * @example removeAdmin(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."})829   * @returns ```true``` if extrinsic success, otherwise ```false```830   */831  async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {832    const result = await this.helper.executeExtrinsic(833      signer,834      'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],835      true,836    );837838    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved');839  }840841  /**842   * Check if user is in allow list.843   * 844   * @param collectionId ID of collection845   * @param user Account to check846   * @example await getAdmins(1)847   * @returns is user in allow list848   */849  async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {850    return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();851  }852853  /**854   * Adds an address to allow list855   * @param signer keyring of signer856   * @param collectionId ID of collection857   * @param addressObj address to add to the allow list858   * @returns ```true``` if extrinsic success, otherwise ```false```859   */860  async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {861    const result = await this.helper.executeExtrinsic(862      signer,863      'api.tx.unique.addToAllowList', [collectionId, addressObj],864      true,865    );866867    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressAdded');868  }869870  /**871   * Removes an address from allow list872   *873   * @param signer keyring of signer874   * @param collectionId ID of collection875   * @param addressObj address to remove from the allow list876   * @returns ```true``` if extrinsic success, otherwise ```false```877   */878  async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {879    const result = await this.helper.executeExtrinsic(880      signer,881      'api.tx.unique.removeFromAllowList', [collectionId, addressObj],882      true,883    );884885    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved');886  }887888  /**889   * Sets onchain permissions for selected collection.890   *891   * @param signer keyring of signer892   * @param collectionId ID of collection893   * @param permissions collection permissions object894   * @example setPermissions(aliceKeyring, 10, {access:'AllowList', mintMode: true, nesting: {collectionAdmin: true, tokenOwner: true}});895   * @returns ```true``` if extrinsic success, otherwise ```false```896   */897  async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {898    const result = await this.helper.executeExtrinsic(899      signer,900      'api.tx.unique.setCollectionPermissions', [collectionId, permissions],901      true,902    );903904    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet');905  }906907  /**908   * Enables nesting for selected collection. If `restricted` set, you can nest only tokens from specified collections.909   *910   * @param signer keyring of signer911   * @param collectionId ID of collection912   * @param permissions nesting permissions object913   * @example enableNesting(aliceKeyring, 10, {collectionAdmin: true, tokenOwner: true});914   * @returns ```true``` if extrinsic success, otherwise ```false```915   */916  async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {917    return await this.setPermissions(signer, collectionId, {nesting: permissions});918  }919920  /**921   * Disables nesting for selected collection.922   *923   * @param signer keyring of signer924   * @param collectionId ID of collection925   * @example disableNesting(aliceKeyring, 10);926   * @returns ```true``` if extrinsic success, otherwise ```false```927   */928  async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {929    return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});930  }931932  /**933   * Sets onchain properties to the collection.934   *935   * @param signer keyring of signer936   * @param collectionId ID of collection937   * @param properties array of property objects938   * @example setProperties(aliceKeyring, 10, [{key: "gender", value: "male"}]);939   * @returns ```true``` if extrinsic success, otherwise ```false```940   */941  async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {942    const result = await this.helper.executeExtrinsic(943      signer,944      'api.tx.unique.setCollectionProperties', [collectionId, properties],945      true,946    );947948    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');949  }950951  /**952   * Get collection properties.953   * 954   * @param collectionId ID of collection955   * @param propertyKeys optionally filter the returned properties to only these keys956   * @example getProperties(1219, ['location', 'date', 'time', 'isParadise']);957   * @returns array of key-value pairs958   */959  async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {960    return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();961  }962963  /**964   * Deletes onchain properties from the collection.965   *966   * @param signer keyring of signer967   * @param collectionId ID of collection968   * @param propertyKeys array of property keys to delete969   * @example deleteProperties(aliceKeyring, 10, ["gender", "age"]);970   * @returns ```true``` if extrinsic success, otherwise ```false```971   */972  async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {973    const result = await this.helper.executeExtrinsic(974      signer,975      'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],976      true,977    );978979    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');980  }981982  /**983   * Changes the owner of the token.984   *985   * @param signer keyring of signer986   * @param collectionId ID of collection987   * @param tokenId ID of token988   * @param addressObj address of a new owner989   * @param amount amount of tokens to be transfered. For NFT must be set to 1n990   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})991   * @returns true if the token success, otherwise false992   */993  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {994    const result = await this.helper.executeExtrinsic(995      signer,996      'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],997      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,998    );9991000    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1001  }10021003  /**1004   *1005   * Change ownership of a token(s) on behalf of the owner.1006   *1007   * @param signer keyring of signer1008   * @param collectionId ID of collection1009   * @param tokenId ID of token1010   * @param fromAddressObj address on behalf of which the token will be sent1011   * @param toAddressObj new token owner1012   * @param amount amount of tokens to be transfered. For NFT must be set to 1n1013   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg"}, {Ethereum: "0x9F0583DbB85..."})1014   * @returns true if the token success, otherwise false1015   */1016  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1017    const result = await this.helper.executeExtrinsic(1018      signer,1019      'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1020      true, // `Unable to transfer token #${tokenId} from collection #${collectionId}`,1021    );1022    return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1023  }10241025  /**1026   *1027   * Destroys a concrete instance of NFT/RFT or burns a specified amount of fungible tokens.1028   *1029   * @param signer keyring of signer1030   * @param collectionId ID of collection1031   * @param tokenId ID of token1032   * @param amount amount of tokens to be burned. For NFT must be set to 1n1033   * @example burnToken(aliceKeyring, 10, 5);1034   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1035   */1036  async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1037    const burnResult = await this.helper.executeExtrinsic(1038      signer,1039      'api.tx.unique.burnItem', [collectionId, tokenId, amount],1040      true, // `Unable to burn token for ${label}`,1041    );1042    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1043    if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1044    return burnedTokens.success;1045  }10461047  /**1048   * Destroys a concrete instance of NFT on behalf of the owner1049   *1050   * @param signer keyring of signer1051   * @param collectionId ID of collection1052   * @param tokenId ID of token1053   * @param fromAddressObj address on behalf of which the token will be burnt1054   * @param amount amount of tokens to be burned. For NFT must be set to 1n1055   * @example burnTokenFrom(aliceKeyring, 10, {Substrate: "5DyN4Y92vZCjv38fg..."}, 5, {Ethereum: "0x9F0583DbB85..."})1056   * @returns ```true``` if extrinsic success, otherwise ```false```1057   */1058  async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1059    const burnResult = await this.helper.executeExtrinsic(1060      signer,1061      'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1062      true, // `Unable to burn token from for ${label}`,1063    );1064    const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1065    return burnedTokens.success && burnedTokens.tokens.length > 0;1066  }10671068  /**1069   * Set, change, or remove approved address to transfer the ownership of the NFT.1070   *1071   * @param signer keyring of signer1072   * @param collectionId ID of collection1073   * @param tokenId ID of token1074   * @param toAddressObj Substrate or Ethereum address which gets approved use of the signer's tokens1075   * @param amount amount of token to be approved. For NFT must be set to 1n1076   * @returns ```true``` if extrinsic success, otherwise ```false```1077   */1078  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1079    const approveResult = await this.helper.executeExtrinsic(1080      signer,1081      'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1082      true, // `Unable to approve token for ${label}`,1083    );10841085    return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1086  }10871088  /**1089   * Get the amount of token pieces approved to transfer or burn. Normally 0.1090   *1091   * @param collectionId ID of collection1092   * @param tokenId ID of token1093   * @param toAccountObj address which is approved to use token pieces1094   * @param fromAccountObj address which may have allowed the use of its owned tokens1095   * @example getTokenApprovedPieces(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5ERZNF88Mm7UGfPP3mdG..."})1096   * @returns number of approved to transfer pieces1097   */1098  async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1099    return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1100  }11011102  /**1103   * Get the last created token ID in a collection1104   *1105   * @param collectionId ID of collection1106   * @example getLastTokenId(10);1107   * @returns id of the last created token1108   */1109  async getLastTokenId(collectionId: number): Promise<number> {1110    return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1111  }11121113  /**1114   * Check if token exists1115   *1116   * @param collectionId ID of collection1117   * @param tokenId ID of token1118   * @example doesTokenExist(10, 20);1119   * @returns true if the token exists, otherwise false1120   */1121  async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1122    return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1123  }1124}11251126class NFTnRFT extends CollectionGroup {1127  /**1128   * Get tokens owned by account1129   *1130   * @param collectionId ID of collection1131   * @param addressObj tokens owner1132   * @example getTokensByAddress(10, {Substrate: "5DyN4Y92vZCjv38fg..."})1133   * @returns array of token ids owned by account1134   */1135  async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1136    return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1137  }11381139  /**1140   * Get token data1141   *1142   * @param collectionId ID of collection1143   * @param tokenId ID of token1144   * @param propertyKeys optionally filter the token properties to only these keys1145   * @param blockHashAt optionally query the data at some block with this hash1146   * @example getToken(10, 5);1147   * @returns human readable token data1148   */1149  async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1150    properties: IProperty[];1151    owner: CrossAccountId;1152    normalizedOwner: CrossAccountId;1153  }| null> {1154    let tokenData;1155    if(typeof blockHashAt === 'undefined') {1156      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1157    }1158    else {1159      if(propertyKeys.length == 0) {1160        const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1161        if(!collection) return null;1162        propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1163      }1164      tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1165    }1166    tokenData = tokenData.toHuman();1167    if (tokenData === null || tokenData.owner === null) return null;1168    const owner = {} as any;1169    for (const key of Object.keys(tokenData.owner)) {1170      owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate' 1171        ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key]) 1172        : tokenData.owner[key];1173    }1174    tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1175    return tokenData;1176  }11771178  /**1179   * Set permissions to change token properties1180   *1181   * @param signer keyring of signer1182   * @param collectionId ID of collection1183   * @param permissions permissions to change a property by the collection admin or token owner1184   * @example setTokenPropertyPermissions(1185   *   aliceKeyring, 10, [{key: "gender", permission: {tokenOwner: true, mutable: true, collectionAdmin: true}}]1186   * )1187   * @returns true if extrinsic success otherwise false1188   */1189  async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1190    const result = await this.helper.executeExtrinsic(1191      signer,1192      'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1193      true,1194    );11951196    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1197  }11981199  /**1200   * Get token property permissions.1201   * 1202   * @param collectionId ID of collection1203   * @param propertyKeys optionally filter the returned property permissions to only these keys1204   * @example getPropertyPermissions(1219, ['location', 'date', 'time', 'isParadise']);1205   * @returns array of key-permission pairs1206   */1207  async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1208    return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1209  }12101211  /**1212   * Set token properties1213   *1214   * @param signer keyring of signer1215   * @param collectionId ID of collection1216   * @param tokenId ID of token1217   * @param properties key-value pairs of metadata which to add to a token. Keys must be permitted in the collection1218   * @example setTokenProperties(aliceKeyring, 10, 5, [{key: "gender", value: "female"}, {key: "age", value: "23"}])1219   * @returns ```true``` if extrinsic success, otherwise ```false```1220   */1221  async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1222    const result = await this.helper.executeExtrinsic(1223      signer,1224      'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1225      true,1226    );12271228    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1229  }12301231  /**1232   * Get properties, metadata assigned to a token.1233   * 1234   * @param collectionId ID of collection1235   * @param tokenId ID of token1236   * @param propertyKeys optionally filter the returned properties to only these keys1237   * @example getTokenProperties(1219, ['location', 'date', 'time', 'isParadise']);1238   * @returns array of key-value pairs1239   */1240  async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1241    return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1242  }12431244  /**1245   * Delete the provided properties of a token1246   * @param signer keyring of signer1247   * @param collectionId ID of collection1248   * @param tokenId ID of token1249   * @param propertyKeys property keys to be deleted1250   * @example deleteTokenProperties(aliceKeyring, 10, 5, ["gender", "age"])1251   * @returns ```true``` if extrinsic success, otherwise ```false```1252   */1253  async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1254    const result = await this.helper.executeExtrinsic(1255      signer,1256      'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1257      true,1258    );12591260    return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1261  }12621263  /**1264   * Mint new collection1265   *1266   * @param signer keyring of signer1267   * @param collectionOptions basic collection options and properties1268   * @param mode NFT or RFT type of a collection1269   * @example mintCollection(aliceKeyring, {name: 'New', description: "New collection", tokenPrefix: "NEW"}, "NFT")1270   * @returns object of the created collection1271   */1272  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1273    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1274    collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1275    for (const key of ['name', 'description', 'tokenPrefix']) {1276      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);1277    }1278    const creationResult = await this.helper.executeExtrinsic(1279      signer,1280      'api.tx.unique.createCollectionEx', [collectionOptions],1281      true, // errorLabel,1282    );1283    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1284  }12851286  getCollectionObject(_collectionId: number): any {1287    return null;1288  }12891290  getTokenObject(_collectionId: number, _tokenId: number): any {1291    return null;1292  }1293}129412951296class NFTGroup extends NFTnRFT {1297  /**1298   * Get collection object1299   * @param collectionId ID of collection1300   * @example getCollectionObject(2);1301   * @returns instance of UniqueNFTCollection1302   */1303  getCollectionObject(collectionId: number): UniqueNFTCollection {1304    return new UniqueNFTCollection(collectionId, this.helper);1305  }13061307  /**1308   * Get token object1309   * @param collectionId ID of collection1310   * @param tokenId ID of token1311   * @example getTokenObject(10, 5);1312   * @returns instance of UniqueNFTToken1313   */1314  getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1315    return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1316  }13171318  /**1319   * Get token's owner1320   * @param collectionId ID of collection1321   * @param tokenId ID of token1322   * @param blockHashAt optionally query the data at the block with this hash1323   * @example getTokenOwner(10, 5);1324   * @returns Address in CrossAccountId format, e.g. {Substrate: "5DnSF6RRjwteE3BrCj..."}1325   */1326  async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1327    let owner;1328    if (typeof blockHashAt === 'undefined') {1329      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1330    } else {1331      owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1332    }1333    return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1334  }13351336  /**1337   * Is token approved to transfer1338   * @param collectionId ID of collection1339   * @param tokenId ID of token1340   * @param toAccountObj address to be approved1341   * @returns ```true``` if extrinsic success, otherwise ```false```1342   */1343  async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1344    return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1345  }13461347  /**1348   * Changes the owner of the token.1349   *1350   * @param signer keyring of signer1351   * @param collectionId ID of collection1352   * @param tokenId ID of token1353   * @param addressObj address of a new owner1354   * @example transferToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1355   * @returns ```true``` if extrinsic success, otherwise ```false```1356   */1357  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1358    return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1359  }13601361  /**1362   *1363   * Change ownership of a NFT on behalf of the owner.1364   *1365   * @param signer keyring of signer1366   * @param collectionId ID of collection1367   * @param tokenId ID of token1368   * @param fromAddressObj address on behalf of which the token will be sent1369   * @param toAddressObj new token owner1370   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Ethereum: "0x9F0583DbB85..."})1371   * @returns ```true``` if extrinsic success, otherwise ```false```1372   */1373  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1374    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1375  }13761377  /**1378   * Recursively find the address that owns the token1379   * @param collectionId ID of collection1380   * @param tokenId ID of token1381   * @param blockHashAt1382   * @example getTokenTopmostOwner(10, 5);1383   * @returns address in CrossAccountId format, e.g. {Substrate: "5DyN4Y92vZCjv38fg..."}1384   */1385  async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1386    let owner;1387    if (typeof blockHashAt === 'undefined') {1388      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1389    } else {1390      owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1391    }13921393    if (owner === null) return null;13941395    return owner.toHuman();1396  }13971398  /**1399   * Get tokens nested in the provided token1400   * @param collectionId ID of collection1401   * @param tokenId ID of token1402   * @param blockHashAt optionally query the data at the block with this hash1403   * @example getTokenChildren(10, 5);1404   * @returns tokens whose depth of nesting is <= 51405   */1406  async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1407    let children;1408    if(typeof blockHashAt === 'undefined') {1409      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1410    } else {1411      children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1412    }14131414    return children.toJSON().map((x: any) => {1415      return {collectionId: x.collection, tokenId: x.token};1416    });1417  }14181419  /**1420   * Nest one token into another1421   * @param signer keyring of signer1422   * @param tokenObj token to be nested1423   * @param rootTokenObj token to be parent1424   * @example nestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4});1425   * @returns ```true``` if extrinsic success, otherwise ```false```1426   */1427  async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1428    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1429    const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1430    if(!result) {1431      throw Error('Unable to nest token!');1432    }1433    return result;1434  }14351436  /**1437   * Remove token from nested state1438   * @param signer keyring of signer1439   * @param tokenObj token to unnest1440   * @param rootTokenObj parent of a token1441   * @param toAddressObj address of a new token owner1442   * @example unnestToken(aliceKeyring, {collectionId: 10, tokenId: 5}, {collectionId: 10, tokenId: 4}, {Substrate: "5DyN4Y92vZCjv38fg..."});1443   * @returns ```true``` if extrinsic success, otherwise ```false```1444   */1445  async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1446    const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1447    const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1448    if(!result) {1449      throw Error('Unable to unnest token!');1450    }1451    return result;1452  }14531454  /**1455   * Mint new collection1456   * @param signer keyring of signer1457   * @param collectionOptions Collection options1458   * @example1459   * mintCollection(aliceKeyring, {1460   *   name: 'New',1461   *   description: 'New collection',1462   *   tokenPrefix: 'NEW',1463   * })1464   * @returns object of the created collection1465   */1466  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1467    return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1468  }14691470  /**1471   * Mint new token1472   * @param signer keyring of signer1473   * @param data token data1474   * @returns created token object1475   */1476  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1477    const creationResult = await this.helper.executeExtrinsic(1478      signer,1479      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1480        nft: {1481          properties: data.properties,1482        },1483      }],1484      true,1485    );1486    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1487    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1488    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1489    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1490  }14911492  /**1493   * Mint multiple NFT tokens1494   * @param signer keyring of signer1495   * @param collectionId ID of collection1496   * @param tokens array of tokens with owner and properties1497   * @example1498   * mintMultipleTokens(aliceKeyring, 10, [{1499   *     owner: {Substrate: "5DyN4Y92vZCjv38fg..."},1500   *     properties: [{key: "gender", value: "male"},{key: "age", value: "45"}],1501   *   },{1502   *     owner: {Ethereum: "0x9F0583DbB855d..."},1503   *     properties: [{key: "gender", value: "female"},{key: "age", value: "22"}],1504   * }]);1505   * @returns ```true``` if extrinsic success, otherwise ```false```1506   */1507  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1508    const creationResult = await this.helper.executeExtrinsic(1509      signer,1510      'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1511      true,1512    );1513    const collection = this.getCollectionObject(collectionId);1514    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1515  }15161517  /**1518   * Mint multiple NFT tokens with one owner1519   * @param signer keyring of signer1520   * @param collectionId ID of collection1521   * @param owner tokens owner1522   * @param tokens array of tokens with owner and properties1523   * @example1524   * mintMultipleTokensWithOneOwner(aliceKeyring, 10, "5DyN4Y92vZCjv38fg...", [{1525   *   properties: [{1526   *   key: "gender",1527   *   value: "female",1528   *  },{1529   *   key: "age",1530   *   value: "33",1531   *  }],1532   * }]);1533   * @returns array of newly created tokens1534   */1535  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1536    const rawTokens = [];1537    for (const token of tokens) {1538      const raw = {NFT: {properties: token.properties}};1539      rawTokens.push(raw);1540    }1541    const creationResult = await this.helper.executeExtrinsic(1542      signer,1543      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1544      true,1545    );1546    const collection = this.getCollectionObject(collectionId);1547    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1548  }15491550  /**1551   * Set, change, or remove approved address to transfer the ownership of the NFT.1552   *1553   * @param signer keyring of signer1554   * @param collectionId ID of collection1555   * @param tokenId ID of token1556   * @param toAddressObj address to approve1557   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."})1558   * @returns ```true``` if extrinsic success, otherwise ```false```1559   */1560  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1561    return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1562  }1563}156415651566class RFTGroup extends NFTnRFT {1567  /**1568   * Get collection object1569   * @param collectionId ID of collection1570   * @example getCollectionObject(2);1571   * @returns instance of UniqueRFTCollection1572   */1573  getCollectionObject(collectionId: number): UniqueRFTCollection {1574    return new UniqueRFTCollection(collectionId, this.helper);1575  }15761577  /**1578   * Get token object1579   * @param collectionId ID of collection1580   * @param tokenId ID of token1581   * @example getTokenObject(10, 5);1582   * @returns instance of UniqueNFTToken1583   */1584  getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1585    return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1586  }15871588  /**1589   * Get top 10 token owners with the largest number of pieces1590   * @param collectionId ID of collection1591   * @param tokenId ID of token1592   * @example getTokenTop10Owners(10, 5);1593   * @returns array of top 10 owners1594   */1595  async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1596    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1597  }15981599  /**1600   * Get number of pieces owned by address1601   * @param collectionId ID of collection1602   * @param tokenId ID of token1603   * @param addressObj address token owner1604   * @example getTokenBalance(10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."});1605   * @returns number of pieces ownerd by address1606   */1607  async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1608    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1609  }16101611  /**1612   * Transfer pieces of token to another address1613   * @param signer keyring of signer1614   * @param collectionId ID of collection1615   * @param tokenId ID of token1616   * @param addressObj address of a new owner1617   * @param amount number of pieces to be transfered1618   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2000n)1619   * @returns ```true``` if extrinsic success, otherwise ```false```1620   */1621  async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1622    return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1623  }16241625  /**1626   * Change ownership of some pieces of RFT on behalf of the owner.1627   * @param signer keyring of signer1628   * @param collectionId ID of collection1629   * @param tokenId ID of token1630   * @param fromAddressObj address on behalf of which the token will be sent1631   * @param toAddressObj new token owner1632   * @param amount number of pieces to be transfered1633   * @example transferTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, {Substrate: "5DfhbVfww7ThF8q6f3i..."}, 2000n)1634   * @returns ```true``` if extrinsic success, otherwise ```false```1635   */1636  async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1637    return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1638  }16391640  /**1641   * Mint new collection1642   * @param signer keyring of signer1643   * @param collectionOptions Collection options1644   * @example1645   * mintCollection(aliceKeyring, {1646   *   name: 'New',1647   *   description: 'New collection',1648   *   tokenPrefix: 'NEW',1649   * })1650   * @returns object of the created collection1651   */1652  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1653    return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1654  }16551656  /**1657   * Mint new token1658   * @param signer keyring of signer1659   * @param data token data1660   * @example mintToken(aliceKeyring, {collectionId: 10, owner: {Substrate: '5GHoZe9c73RYbVzq...'}, pieces: 10000n});1661   * @returns created token object1662   */1663  async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1664    const creationResult = await this.helper.executeExtrinsic(1665      signer,1666      'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1667        refungible: {1668          pieces: data.pieces,1669          properties: data.properties,1670        },1671      }],1672      true,1673    );1674    const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1675    if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1676    if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1677    return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1678  }16791680  async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1681    throw Error('Not implemented');1682    const creationResult = await this.helper.executeExtrinsic(1683      signer,1684      'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1685      true, // `Unable to mint RFT tokens for ${label}`,1686    );1687    const collection = this.getCollectionObject(collectionId);1688    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1689  }16901691  /**1692   * Mint multiple RFT tokens with one owner1693   * @param signer keyring of signer1694   * @param collectionId ID of collection1695   * @param owner tokens owner1696   * @param tokens array of tokens with properties and pieces1697   * @example mintMultipleTokensWithOneOwner(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, [{pieces: 100000n, properties: [{key: "gender", value: "male"}]}]);1698   * @returns array of newly created RFT tokens1699   */1700  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1701    const rawTokens = [];1702    for (const token of tokens) {1703      const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1704      rawTokens.push(raw);1705    }1706    const creationResult = await this.helper.executeExtrinsic(1707      signer,1708      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1709      true,1710    );1711    const collection = this.getCollectionObject(collectionId);1712    return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1713  }17141715  /**1716   * Destroys a concrete instance of RFT.1717   * @param signer keyring of signer1718   * @param collectionId ID of collection1719   * @param tokenId ID of token1720   * @param amount number of pieces to be burnt1721   * @example burnToken(aliceKeyring, 10, 5);1722   * @returns ```true``` if the extrinsic is successful, otherwise ```false```1723   */1724  async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1725    return await super.burnToken(signer, collectionId, tokenId, amount);1726  }17271728  /**1729   * Destroys a concrete instance of RFT on behalf of the owner.1730   * @param signer keyring of signer1731   * @param collectionId ID of collection1732   * @param tokenId ID of token1733   * @param fromAddressObj address on behalf of which the token will be burnt1734   * @param amount number of pieces to be burnt1735   * @example burnTokenFrom(aliceKeyring, 10, 5, {Substrate: "5DyN4Y92vZCjv38fg..."}, 2n)1736   * @returns ```true``` if extrinsic success, otherwise ```false```1737   */1738  async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1739    return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1740  }17411742  /**1743   * Set, change, or remove approved address to transfer the ownership of the RFT.1744   *1745   * @param signer keyring of signer1746   * @param collectionId ID of collection1747   * @param tokenId ID of token1748   * @param toAddressObj address to approve1749   * @param amount number of pieces to be approved1750   * @example approveToken(aliceKeyring, 10, 5, {Substrate: "5GHoZe9c73RYbVzq..."}, "", 10000n);1751   * @returns true if the token success, otherwise false1752   */1753  async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1754    return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1755  }17561757  /**1758   * Get total number of pieces1759   * @param collectionId ID of collection1760   * @param tokenId ID of token1761   * @example getTokenTotalPieces(10, 5);1762   * @returns number of pieces1763   */1764  async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1765    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1766  }17671768  /**1769   * Change number of token pieces. Signer must be the owner of all token pieces.1770   * @param signer keyring of signer1771   * @param collectionId ID of collection1772   * @param tokenId ID of token1773   * @param amount new number of pieces1774   * @example repartitionToken(aliceKeyring, 10, 5, 12345n);1775   * @returns true if the repartion was success, otherwise false1776   */1777  async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1778    const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1779    const repartitionResult = await this.helper.executeExtrinsic(1780      signer,1781      'api.tx.unique.repartition', [collectionId, tokenId, amount],1782      true,1783    );1784    if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1785    return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1786  }1787}178817891790class FTGroup extends CollectionGroup {1791  /**1792   * Get collection object1793   * @param collectionId ID of collection1794   * @example getCollectionObject(2);1795   * @returns instance of UniqueFTCollection1796   */1797  getCollectionObject(collectionId: number): UniqueFTCollection {1798    return new UniqueFTCollection(collectionId, this.helper);1799  }18001801  /**1802   * Mint new fungible collection1803   * @param signer keyring of signer1804   * @param collectionOptions Collection options1805   * @param decimalPoints number of token decimals1806   * @example1807   * mintCollection(aliceKeyring, {1808   *   name: 'New',1809   *   description: 'New collection',1810   *   tokenPrefix: 'NEW',1811   * }, 18)1812   * @returns newly created fungible collection1813   */1814  async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1815    collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; // Clone object1816    if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1817    collectionOptions.mode = {fungible: decimalPoints};1818    for (const key of ['name', 'description', 'tokenPrefix']) {1819      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);1820    }1821    const creationResult = await this.helper.executeExtrinsic(1822      signer,1823      'api.tx.unique.createCollectionEx', [collectionOptions],1824      true,1825    );1826    return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1827  }18281829  /**1830   * Mint tokens1831   * @param signer keyring of signer1832   * @param collectionId ID of collection1833   * @param owner address owner of new tokens1834   * @param amount amount of tokens to be meanted1835   * @example mintTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq"}, 1000n);1836   * @returns ```true``` if extrinsic success, otherwise ```false```1837   */1838  async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {1839    const creationResult = await this.helper.executeExtrinsic(1840      signer,1841      'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {1842        fungible: {1843          value: amount,1844        },1845      }],1846      true, // `Unable to mint fungible tokens for ${label}`,1847    );1848    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1849  }18501851  /**1852   * Mint multiple Fungible tokens with one owner1853   * @param signer keyring of signer1854   * @param collectionId ID of collection1855   * @param owner tokens owner1856   * @param tokens array of tokens with properties and pieces1857   * @returns ```true``` if extrinsic success, otherwise ```false```1858   */1859  async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {1860    const rawTokens = [];1861    for (const token of tokens) {1862      const raw = {Fungible: {Value: token.value}};1863      rawTokens.push(raw);1864    }1865    const creationResult = await this.helper.executeExtrinsic(1866      signer,1867      'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1868      true,1869    );1870    return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1871  }18721873  /**1874   * Get the top 10 owners with the largest balance for the Fungible collection1875   * @param collectionId ID of collection1876   * @example getTop10Owners(10);1877   * @returns array of ```ICrossAccountId```1878   */1879  async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {1880    return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1881  }18821883  /**1884   * Get account balance1885   * @param collectionId ID of collection1886   * @param addressObj address of owner1887   * @example getBalance(10, {Substrate: "5GHoZe9c73RYbVzq..."})1888   * @returns amount of fungible tokens owned by address1889   */1890  async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {1891    return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();1892  }18931894  /**1895   * Transfer tokens to address1896   * @param signer keyring of signer1897   * @param collectionId ID of collection1898   * @param toAddressObj address recipient1899   * @param amount amount of tokens to be sent1900   * @example transfer(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);1901   * @returns ```true``` if extrinsic success, otherwise ```false```1902   */1903  async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {1904    return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);1905  }19061907  /**1908   * Transfer some tokens on behalf of the owner.1909   * @param signer keyring of signer1910   * @param collectionId ID of collection1911   * @param fromAddressObj address on behalf of which tokens will be sent1912   * @param toAddressObj address where token to be sent1913   * @param amount number of tokens to be sent1914   * @example transferFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, {Substrate: "5DfhbVfww7ThF8q6f3ij..."}, 10000n);1915   * @returns ```true``` if extrinsic success, otherwise ```false```1916   */1917  async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {1918    return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);1919  }19201921  /**1922   * Destroy some amount of tokens1923   * @param signer keyring of signer1924   * @param collectionId ID of collection1925   * @param amount amount of tokens to be destroyed1926   * @example burnTokens(aliceKeyring, 10, 1000n);1927   * @returns ```true``` if extrinsic success, otherwise ```false```1928   */1929  async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {1930    return await super.burnToken(signer, collectionId, 0, amount);1931  }19321933  /**1934   * Burn some tokens on behalf of the owner.1935   * @param signer keyring of signer1936   * @param collectionId ID of collection1937   * @param fromAddressObj address on behalf of which tokens will be burnt1938   * @param amount amount of tokens to be burnt1939   * @example burnTokensFrom(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n);1940   * @returns ```true``` if extrinsic success, otherwise ```false```1941   */1942  async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1943    return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);1944  }19451946  /**1947   * Get total collection supply1948   * @param collectionId1949   * @returns1950   */1951  async getTotalPieces(collectionId: number): Promise<bigint> {1952    return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();1953  }19541955  /**1956   * Set, change, or remove approved address to transfer tokens.1957   *1958   * @param signer keyring of signer1959   * @param collectionId ID of collection1960   * @param toAddressObj address to be approved1961   * @param amount amount of tokens to be approved1962   * @example approveTokens(aliceKeyring, 10, {Substrate: "5GHoZe9c73RYbVzq..."}, 1000n)1963   * @returns ```true``` if extrinsic success, otherwise ```false```1964   */1965  async approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {1966    return super.approveToken(signer, collectionId, 0, toAddressObj, amount);1967  }19681969  /**1970   * Get amount of fungible tokens approved to transfer1971   * @param collectionId ID of collection1972   * @param fromAddressObj owner of tokens1973   * @param toAddressObj the address approved for the transfer of tokens on behalf of the owner1974   * @returns number of tokens approved for the transfer1975   */1976  async getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {1977    return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);1978  }1979}198019811982class ChainGroup extends HelperGroup {1983  /**1984   * Get system properties of a chain1985   * @example getChainProperties();1986   * @returns ss58Format, token decimals, and token symbol1987   */1988  getChainProperties(): IChainProperties {1989    const properties = (this.helper.api as any).registry.getChainProperties().toJSON();1990    return {1991      ss58Format: properties.ss58Format.toJSON(),1992      tokenDecimals: properties.tokenDecimals.toJSON(),1993      tokenSymbol: properties.tokenSymbol.toJSON(),1994    };1995  }19961997  /**1998   * Get chain header1999   * @example getLatestBlockNumber();2000   * @returns the number of the last block2001   */2002  async getLatestBlockNumber(): Promise<number> {2003    return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2004  }20052006  /**2007   * Get block hash by block number2008   * @param blockNumber number of block2009   * @example getBlockHashByNumber(12345);2010   * @returns hash of a block2011   */2012  async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2013    const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2014    if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2015    return blockHash;2016  }20172018  // TODO add docs2019  async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2020    const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2021    if (!blockHash) return null;2022    return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2023  }20242025  /**2026   * Get account nonce2027   * @param address substrate address2028   * @example getNonce("5GrwvaEF5zXb26Fz...");2029   * @returns number, account's nonce2030   */2031  async getNonce(address: TSubstrateAccount): Promise<number> {2032    return (await (this.helper.api as any).query.system.account(address)).nonce.toNumber();2033  }2034}203520362037class BalanceGroup extends HelperGroup {2038  getCollectionCreationPrice(): bigint {2039    return 2n * this.helper.balance.getOneTokenNominal();2040  }2041  /**2042   * Representation of the native token in the smallest unit - one OPAL (OPL), QUARTZ (QTZ), or UNIQUE (UNQ).2043   * @example getOneTokenNominal()2044   * @returns ```BigInt``` representation of the native token in the smallest unit, e.g. ```1_000_000_000_000_000_000n``` for QTZ.2045   */2046  getOneTokenNominal(): bigint {2047    const chainProperties = this.helper.chain.getChainProperties();2048    return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2049  }20502051  /**2052   * Get substrate address balance2053   * @param address substrate address2054   * @example getSubstrate("5GrwvaEF5zXb26Fz...")2055   * @returns amount of tokens on address2056   */2057  async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2058    return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2059  }20602061  /**2062   * Get full substrate balance including free, miscFrozen, feeFrozen, and reserved2063   * @param address substrate address2064   * @returns2065   */2066  async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2067    const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2068    return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2069  }20702071  /**2072   * Get ethereum address balance2073   * @param address ethereum address2074   * @example getEthereum("0x9F0583DbB855d...")2075   * @returns amount of tokens on address2076   */2077  async getEthereum(address: TEthereumAccount): Promise<bigint> {2078    return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2079  }20802081  /**2082   * Transfer tokens to substrate address2083   * @param signer keyring of signer2084   * @param address substrate address of a recipient2085   * @param amount amount of tokens to be transfered2086   * @example transferToSubstrate(aliceKeyring, "5GrwvaEF5zXb26Fz...", 100_000_000_000n);2087   * @returns ```true``` if extrinsic success, otherwise ```false```2088   */2089  async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2090    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}`*/);20912092    let transfer = {from: null, to: null, amount: 0n} as any;2093    result.result.events.forEach(({event: {data, method, section}}) => {2094      if ((section === 'balances') && (method === 'Transfer')) {2095        transfer = {2096          from: this.helper.address.normalizeSubstrate(data[0]),2097          to: this.helper.address.normalizeSubstrate(data[1]),2098          amount: BigInt(data[2]),2099        };2100      }2101    });2102    const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from 2103      && this.helper.address.normalizeSubstrate(address) === transfer.to 2104      && BigInt(amount) === transfer.amount;2105    return isSuccess;2106  }2107}210821092110class AddressGroup extends HelperGroup {2111  /**2112   * Normalizes the address to the specified ss58 format, by default ```42```.2113   * @param address substrate address2114   * @param ss58Format format for address conversion, by default ```42```2115   * @example normalizeSubstrate("unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx") // returns 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY2116   * @returns substrate address converted to normalized (i.e., starting with 5) or specified explicitly representation2117   */2118  normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2119    return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2120  }21212122  /**2123   * Get address in the connected chain format2124   * @param address substrate address2125   * @example normalizeSubstrateToChainFormat("5GrwvaEF5zXb26Fz...") // returns unjKJQJrRd238pkUZZ... for Unique Network2126   * @returns address in chain format2127   */2128  normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2129    return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2130  }21312132  /**2133   * Get substrate mirror of an ethereum address2134   * @param ethAddress ethereum address2135   * @param toChainFormat false for normalized account2136   * @example ethToSubstrate('0x9F0583DbB855d...')2137   * @returns substrate mirror of a provided ethereum address2138   */2139  ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2140    return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2141  }21422143  /**2144   * Get ethereum mirror of a substrate address2145   * @param subAddress substrate account2146   * @example substrateToEth("5DnSF6RRjwteE3BrC...")2147   * @returns ethereum mirror of a provided substrate address2148   */2149  substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2150    return CrossAccountId.translateSubToEth(subAddress);2151  }2152}21532154class StakingGroup extends HelperGroup {2155  /**2156   * Stake tokens for App Promotion2157   * @param signer keyring of signer2158   * @param amountToStake amount of tokens to stake2159   * @param label extra label for log2160   * @returns2161   */2162  async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2163    if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2164    const _stakeResult = await this.helper.executeExtrinsic(2165      signer, 'api.tx.appPromotion.stake',2166      [amountToStake], true,2167    );2168    // TODO extract info from stakeResult2169    return true;2170  }21712172  /**2173   * Unstake tokens for App Promotion2174   * @param signer keyring of signer2175   * @param amountToUnstake amount of tokens to unstake2176   * @param label extra label for log2177   * @returns block number where balances will be unlocked2178   */2179  async unstake(signer: TSigner, label?: string): Promise<number> {2180    if(typeof label === 'undefined') label = `${signer.address}`;2181    const _unstakeResult = await this.helper.executeExtrinsic(2182      signer, 'api.tx.appPromotion.unstake',2183      [], true,2184    );2185    // TODO extract block number fron events2186    return 1;2187  }21882189  /**2190   * Get total staked amount for address2191   * @param address substrate or ethereum address2192   * @returns total staked amount2193   */2194  async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2195    if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2196    return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2197  }21982199  /**2200   * Get total staked per block2201   * @param address substrate or ethereum address2202   * @returns array of stakes. `block` – the number of the block in which the stake was made. `amount` - the number of tokens staked in the block2203   */2204  async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2205    const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2206    return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2207      return { 2208        block: block.toBigInt(),2209        amount: amount.toBigInt(),2210      };2211    });2212  }22132214  /**2215   * Get total pending unstake amount for address2216   * @param address substrate or ethereum address2217   * @returns total pending unstake amount2218   */2219  async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2220    return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2221  }22222223  /**2224   * Get pending unstake amount per block for address2225   * @param address substrate or ethereum address2226   * @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 block2227   */2228  async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2229    const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2230    const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2231      return {2232        block: block.toBigInt(),2233        amount: amount.toBigInt(),2234      };2235    });2236    return result;2237  }2238}22392240export class UniqueHelper extends ChainHelperBase {2241  chain: ChainGroup;2242  balance: BalanceGroup;2243  address: AddressGroup;2244  collection: CollectionGroup;2245  nft: NFTGroup;2246  rft: RFTGroup;2247  ft: FTGroup;2248  staking: StakingGroup;22492250  constructor(logger?: ILogger) {2251    super(logger);2252    this.chain = new ChainGroup(this);2253    this.balance = new BalanceGroup(this);2254    this.address = new AddressGroup(this);2255    this.collection = new CollectionGroup(this);2256    this.nft = new NFTGroup(this);2257    this.rft = new RFTGroup(this);2258    this.ft = new FTGroup(this);2259    this.staking = new StakingGroup(this);2260  }2261}226222632264export class UniqueBaseCollection {2265  helper: UniqueHelper;2266  collectionId: number;22672268  constructor(collectionId: number, uniqueHelper: UniqueHelper) {2269    this.collectionId = collectionId;2270    this.helper = uniqueHelper;2271  }22722273  async getData() {2274    return await this.helper.collection.getData(this.collectionId);2275  }22762277  async getLastTokenId() {2278    return await this.helper.collection.getLastTokenId(this.collectionId);2279  }22802281  async doesTokenExist(tokenId: number) {2282    return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);2283  }22842285  async getAdmins() {2286    return await this.helper.collection.getAdmins(this.collectionId);2287  }22882289  async getAllowList() {2290    return await this.helper.collection.getAllowList(this.collectionId);2291  }22922293  async getEffectiveLimits() {2294    return await this.helper.collection.getEffectiveLimits(this.collectionId);2295  }22962297  async getProperties(propertyKeys?: string[] | null) {2298    return await this.helper.collection.getProperties(this.collectionId, propertyKeys);2299  }23002301  async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {2302    return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);2303  }23042305  async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {2306    return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);2307  }23082309  async confirmSponsorship(signer: TSigner) {2310    return await this.helper.collection.confirmSponsorship(signer, this.collectionId);2311  }23122313  async removeSponsor(signer: TSigner) {2314    return await this.helper.collection.removeSponsor(signer, this.collectionId);2315  }23162317  async setLimits(signer: TSigner, limits: ICollectionLimits) {2318    return await this.helper.collection.setLimits(signer, this.collectionId, limits);2319  }23202321  async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {2322    return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);2323  }23242325  async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {2326    return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);2327  }23282329  async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {2330    return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);2331  }23322333  async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {2334    return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);2335  }23362337  async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {2338    return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);2339  }23402341  async setProperties(signer: TSigner, properties: IProperty[]) {2342    return await this.helper.collection.setProperties(signer, this.collectionId, properties);2343  }23442345  async deleteProperties(signer: TSigner, propertyKeys: string[]) {2346    return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);2347  }23482349  async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {2350    return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);2351  }23522353  async enableNesting(signer: TSigner, permissions: INestingPermissions) {2354    return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);2355  }23562357  async disableNesting(signer: TSigner) {2358    return await this.helper.collection.disableNesting(signer, this.collectionId);2359  }23602361  async burn(signer: TSigner) {2362    return await this.helper.collection.burn(signer, this.collectionId);2363  }2364}236523662367export class UniqueNFTCollection extends UniqueBaseCollection {2368  getTokenObject(tokenId: number) {2369    return new UniqueNFToken(tokenId, this);2370  }23712372  async getTokensByAddress(addressObj: ICrossAccountId) {2373    return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);2374  }23752376  async getToken(tokenId: number, blockHashAt?: string) {2377    return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);2378  }23792380  async getTokenOwner(tokenId: number, blockHashAt?: string) {2381    return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);2382  }23832384  async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {2385    return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);2386  }23872388  async getTokenChildren(tokenId: number, blockHashAt?: string) {2389    return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);2390  }23912392  async getPropertyPermissions(propertyKeys: string[] | null = null) {2393    return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);2394  }23952396  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {2397    return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);2398  }23992400  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {2401    return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);2402  }24032404  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2405    return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);2406  }24072408  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {2409    return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);2410  }24112412  async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {2413    return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);2414  }24152416  async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {2417    return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});2418  }24192420  async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {2421    return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);2422  }24232424  async burnToken(signer: TSigner, tokenId: number) {2425    return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);2426  }24272428  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {2429    return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);2430  }24312432  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {2433    return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);2434  }24352436  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {2437    return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);2438  }24392440  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {2441    return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);2442  }24432444  async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {2445    return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);2446  }24472448  async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {2449    return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);2450  }2451}245224532454export class UniqueRFTCollection extends UniqueBaseCollection {2455  getTokenObject(tokenId: number) {2456    return new UniqueRFToken(tokenId, this);2457  }24582459  async getToken(tokenId: number, blockHashAt?: string) {2460    return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);2461  }24622463  async getTokensByAddress(addressObj: ICrossAccountId) {2464    return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);2465  }24662467  async getTop10TokenOwners(tokenId: number) {2468    return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);2469  }24702471  async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {2472    return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);2473  }24742475  async getTokenTotalPieces(tokenId: number) {2476    return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);2477  }24782479  async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2480    return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);2481  }24822483  async getPropertyPermissions(propertyKeys: string[] | null = null) {2484    return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);2485  }24862487  async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {2488    return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);2489  }24902491  async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {2492    return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);2493  }24942495  async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2496    return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);2497  }24982499  async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {2500    return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);2501  }25022503  async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {2504    return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);2505  }25062507  async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {2508    return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});2509  }25102511  async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {2512    return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);2513  }25142515  async burnToken(signer: TSigner, tokenId: number, amount=1n) {2516    return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);2517  }25182519  async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId,  amount=1n) {2520    return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);2521  }25222523  async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {2524    return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);2525  }25262527  async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {2528    return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);2529  }25302531  async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {2532    return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);2533  }2534}253525362537export class UniqueFTCollection extends UniqueBaseCollection {2538  async getBalance(addressObj: ICrossAccountId) {2539    return await this.helper.ft.getBalance(this.collectionId, addressObj);2540  }25412542  async getTotalPieces() {2543    return await this.helper.ft.getTotalPieces(this.collectionId);2544  }25452546  async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2547    return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);2548  }25492550  async getTop10Owners() {2551    return await this.helper.ft.getTop10Owners(this.collectionId);2552  }25532554  async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {2555    return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);2556  }25572558  async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {2559    return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);2560  }25612562  async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {2563    return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);2564  }25652566  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2567    return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);2568  }25692570  async burnTokens(signer: TSigner, amount=1n) {2571    return await this.helper.ft.burnTokens(signer, this.collectionId, amount);2572  }25732574  async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {2575    return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);2576  }25772578  async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {2579    return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);2580  }2581}258225832584export class UniqueBaseToken {2585  collection: UniqueNFTCollection | UniqueRFTCollection;2586  collectionId: number;2587  tokenId: number;25882589  constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {2590    this.collection = collection;2591    this.collectionId = collection.collectionId;2592    this.tokenId = tokenId;2593  }25942595  async getNextSponsored(addressObj: ICrossAccountId) {2596    return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);2597  }25982599  async getProperties(propertyKeys?: string[] | null) {2600    return await this.collection.getTokenProperties(this.tokenId, propertyKeys);2601  }26022603  async setProperties(signer: TSigner, properties: IProperty[]) {2604    return await this.collection.setTokenProperties(signer, this.tokenId, properties);2605  }26062607  async deleteProperties(signer: TSigner, propertyKeys: string[]) {2608    return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);2609  }26102611  async doesExist() {2612    return await this.collection.doesTokenExist(this.tokenId);2613  }26142615  nestingAccount() {2616    return this.collection.helper.util.getTokenAccount(this);2617  }2618}261926202621export class UniqueNFToken extends UniqueBaseToken {2622  collection: UniqueNFTCollection;26232624  constructor(tokenId: number, collection: UniqueNFTCollection) {2625    super(tokenId, collection);2626    this.collection = collection;2627  }26282629  async getData(blockHashAt?: string) {2630    return await this.collection.getToken(this.tokenId, blockHashAt);2631  }26322633  async getOwner(blockHashAt?: string) {2634    return await this.collection.getTokenOwner(this.tokenId, blockHashAt);2635  }26362637  async getTopmostOwner(blockHashAt?: string) {2638    return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);2639  }26402641  async getChildren(blockHashAt?: string) {2642    return await this.collection.getTokenChildren(this.tokenId, blockHashAt);2643  }26442645  async nest(signer: TSigner, toTokenObj: IToken) {2646    return await this.collection.nestToken(signer, this.tokenId, toTokenObj);2647  }26482649  async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {2650    return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);2651  }26522653  async transfer(signer: TSigner, addressObj: ICrossAccountId) {2654    return await this.collection.transferToken(signer, this.tokenId, addressObj);2655  }26562657  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2658    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);2659  }26602661  async approve(signer: TSigner, toAddressObj: ICrossAccountId) {2662    return await this.collection.approveToken(signer, this.tokenId, toAddressObj);2663  }26642665  async isApproved(toAddressObj: ICrossAccountId) {2666    return await this.collection.isTokenApproved(this.tokenId, toAddressObj);2667  }26682669  async burn(signer: TSigner) {2670    return await this.collection.burnToken(signer, this.tokenId);2671  }26722673  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {2674    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);2675  }2676}26772678export class UniqueRFToken extends UniqueBaseToken {2679  collection: UniqueRFTCollection;26802681  constructor(tokenId: number, collection: UniqueRFTCollection) {2682    super(tokenId, collection);2683    this.collection = collection;2684  }26852686  async getData(blockHashAt?: string) {2687    return await this.collection.getToken(this.tokenId, blockHashAt);2688  }26892690  async getTop10Owners() {2691    return await this.collection.getTop10TokenOwners(this.tokenId);2692  }26932694  async getBalance(addressObj: ICrossAccountId) {2695    return await this.collection.getTokenBalance(this.tokenId, addressObj);2696  }26972698  async getTotalPieces() {2699    return await this.collection.getTokenTotalPieces(this.tokenId);2700  }27012702  async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {2703    return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);2704  }27052706  async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {2707    return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);2708  }27092710  async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2711    return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);2712  }27132714  async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {2715    return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);2716  }27172718  async repartition(signer: TSigner, amount: bigint) {2719    return await this.collection.repartitionToken(signer, this.tokenId, amount);2720  }27212722  async burn(signer: TSigner, amount=1n) {2723    return await this.collection.burnToken(signer, this.tokenId, amount);2724  }27252726  async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {2727    return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);2728  }2729}