12345678import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import type {SignerOptions} from '@polkadot/api/types';10import type {AugmentedSubmittables} from '@polkadot/api-base/types/submittable';11import type {ApiInterfaceEvents} from '@polkadot/api/types';12import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';13import type {IKeyringPair} from '@polkadot/types/types';14import {hexToU8a} from '@polkadot/util/hex';15import {u8aConcat} from '@polkadot/util/u8a';16import type {17 IApiListeners,18 IBlock,19 IEvent,20 IChainProperties,21 ICollectionCreationOptions,22 ICollectionLimits,23 ICollectionPermissions,24 ICrossAccountId,25 ICrossAccountIdLower,26 ILogger,27 INestingPermissions,28 IProperty,29 IStakingInfo,30 ISubstrateBalance,31 IToken,32 ITokenPropertyPermission,33 ITransactionResult,34 IUniqueHelperLog,35 TApiAllowedListeners,36 TEthereumAccount,37 TSigner,38 TSubstrateAccount,39 TNetworks,40 IEthCrossAccountId,41} from './types.js';42import type {RuntimeDispatchInfo} from '@polkadot/types/interfaces';4344export class CrossAccountId {45 account: ICrossAccountId;4647 constructor(account: ICrossAccountId) {48 this.account = account;49 }5051 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {52 switch (domain) {53 case 'Substrate': return new CrossAccountId({Substrate: account.address});54 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();55 }56 }5758 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {59 if('substrate' in address) return new CrossAccountId({Substrate: address.substrate});60 else return new CrossAccountId({Ethereum: address.ethereum});61 }6263 static normalizeSubstrateAddress(address: {Substrate: TSubstrateAccount}, ss58Format = 42): TSubstrateAccount {64 return encodeAddress(decodeAddress(address.Substrate), ss58Format);65 }6667 static withNormalizedSubstrate(address: ICrossAccountId, ss58Format = 42): ICrossAccountId {68 if('Substrate' in address) return {Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)};69 return address;70 }7172 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {73 if('Substrate' in this.account) this.account = CrossAccountId.withNormalizedSubstrate(this.account, ss58Format);74 return this;75 }7677 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {78 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));79 }8081 toEthereum(): CrossAccountId {82 this.account = CrossAccountId.toEthereum(this.account);83 return this;84 }8586 static toEthereum(account: ICrossAccountId): ICrossAccountId {87 if('Substrate' in account) return {Ethereum: CrossAccountId.translateSubToEth(account.Substrate)};88 return account;89 }9091 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {92 return evmToAddress(address, ss58Format);93 }9495 toSubstrate(ss58Format?: number): CrossAccountId {96 this.account = CrossAccountId.toSubstrate(this.account, ss58Format);97 return this;98 }99100 static toSubstrate(account: ICrossAccountId, ss58Format?: number): ICrossAccountId {101 if('Ethereum' in account) return {Substrate: CrossAccountId.translateEthToSub(account.Ethereum, ss58Format)};102 return account;103 }104105 toLowerCase(): CrossAccountId {106 this.account = CrossAccountId.toLowerCase(this.account);107 return this;108 }109110 static toLowerCase(account: ICrossAccountId) {111 if('Substrate' in account) return {Substrate: account.Substrate.toLowerCase()};112 if('Ethereum' in account) return {Ethereum: account.Ethereum.toLowerCase()};113 return account;114 }115116 toICrossAccountId(): ICrossAccountId {117 return this.account;118 }119}120121const nesting = {122 toChecksumAddress(address: string): string {123 if(typeof address === 'undefined') return '';124125 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);126127 address = address.toLowerCase().replace(/^0x/i, '');128 const addressHash = keccakAsHex(address).replace(/^0x/i, '');129 const checksumAddress = ['0x'];130131 for(let i = 0; i < address.length; i++) {132 133 if(parseInt(addressHash[i], 16) > 7) {134 checksumAddress.push(address[i].toUpperCase());135 } else {136 checksumAddress.push(address[i]);137 }138 }139 return checksumAddress.join('');140 },141 tokenIdToAddress(collectionId: number, tokenId: number) {142 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);143 },144};145146class UniqueUtil {147 static transactionStatus = {148 NOT_READY: 'NotReady',149 FAIL: 'Fail',150 SUCCESS: 'Success',151 };152153 static chainLogType = {154 EXTRINSIC: 'extrinsic',155 RPC: 'rpc',156 };157158 static getTokenAccount(token: IToken): ICrossAccountId {159 return {Ethereum: this.getTokenAddress(token)};160 }161162 static getTokenAddress(token: IToken): string {163 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);164 }165166 static getDefaultLogger(): ILogger {167 return {168 log(msg: any, level = 'INFO') {169 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));170 },171 level: {172 ERROR: 'ERROR',173 WARNING: 'WARNING',174 INFO: 'INFO',175 },176 };177 }178179 static vec2str(arr: string[] | number[]) {180 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');181 }182183 static str2vec(string: string) {184 if(typeof string !== 'string') return string;185 return Array.from(string).map(x => x.charCodeAt(0));186 }187188 static fromSeed(seed: string, ss58Format = 42) {189 const keyring = new Keyring({type: 'sr25519', ss58Format});190 return keyring.addFromUri(seed);191 }192193 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {194 if(creationResult.status !== this.transactionStatus.SUCCESS) {195 throw Error('Unable to create collection!');196 }197198 let collectionId = null;199 creationResult.result.events.forEach(({event: {data, method, section}}) => {200 if((section === 'common') && (method === 'CollectionCreated')) {201 collectionId = parseInt(data[0].toString(), 10);202 }203 });204205 if(collectionId === null) {206 throw Error('No CollectionCreated event was found!');207 }208209 return collectionId;210 }211212 static extractTokensFromCreationResult(creationResult: ITransactionResult): {213 success: boolean,214 tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],215 } {216 if(creationResult.status !== this.transactionStatus.SUCCESS) {217 throw Error('Unable to create tokens!');218 }219 let success = false;220 const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];221 creationResult.result.events.forEach(({event: {data, method, section}}) => {222 if(method === 'ExtrinsicSuccess') {223 success = true;224 } else if((section === 'common') && (method === 'ItemCreated')) {225 tokens.push({226 collectionId: parseInt(data[0].toString(), 10),227 tokenId: parseInt(data[1].toString(), 10),228 owner: data[2].toHuman(),229 amount: data[3].toBigInt(),230 });231 }232 });233 return {success, tokens};234 }235236 static extractTokensFromBurnResult(burnResult: ITransactionResult): {237 success: boolean,238 tokens: { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[],239 } {240 if(burnResult.status !== this.transactionStatus.SUCCESS) {241 throw Error('Unable to burn tokens!');242 }243 let success = false;244 const tokens = [] as { collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint }[];245 burnResult.result.events.forEach(({event: {data, method, section}}: any) => {246 if(method === 'ExtrinsicSuccess') {247 success = true;248 } else if((section === 'common') && (method === 'ItemDestroyed')) {249 tokens.push({250 collectionId: parseInt(data[0].toString(), 10),251 tokenId: parseInt(data[1].toString(), 10),252 owner: data[2].toHuman(),253 amount: data[3].toBigInt(),254 });255 }256 });257 return {success, tokens};258 }259260 static findCollectionInEvents(events: { event: IEvent }[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {261 let eventId = null;262 events.forEach(({event: {data, method, section}}) => {263 if((section === expectedSection) && (method === expectedMethod)) {264 eventId = parseInt(data[0].toString(), 10);265 }266 });267268 if(eventId === null) {269 throw Error(`No ${expectedMethod} event was found!`);270 }271 return eventId === collectionId;272 }273274 static isTokenTransferSuccess(events: { event: IEvent }[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {275 const normalizeAddress = (address: string | ICrossAccountId) => {276 if(typeof address === 'string') return address;277 if('Substrate' in address) return CrossAccountId.withNormalizedSubstrate(address);278 if('Ethereum' in address) return CrossAccountId.toLowerCase(address);279 return address;280 };281 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;282 events.forEach(({event: {data, method, section}}) => {283 if((section === 'common') && (method === 'Transfer')) {284 transfer = {285 collectionId: data[0].toJSON(),286 tokenId: data[1].toJSON(),287 from: normalizeAddress(data[2].toHuman()),288 to: normalizeAddress(data[3].toHuman()),289 amount: BigInt(data[4].toJSON()),290 };291 }292 });293 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;294 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);295 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);296 isSuccess = isSuccess && amount === transfer.amount;297 return isSuccess;298 }299300 static bigIntToDecimals(number: bigint, decimals = 18) {301 const numberStr = number.toString();302 const dotPos = numberStr.length - decimals;303304 if(dotPos <= 0) {305 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;306 } else {307 const intPart = numberStr.substring(0, dotPos);308 const fractPart = numberStr.substring(dotPos);309 return intPart + '.' + fractPart;310 }311 }312}313314class UniqueEventHelper {315 static extractIndex(index: any): [number, number] | string {316 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];317 return index.toJSON();318 }319320 static extractSub(data: any, subTypes: any): { [key: string]: any } {321 let obj: any = {};322 let index = 0;323324 if(data.entries) {325 for(const [key, value] of data.entries()) {326 obj[key] = this.extractData(value, subTypes[index]);327 index++;328 }329 } else obj = data.toJSON();330331 return obj;332 }333334 static toHuman(data: any) {335 return data && data.toHuman ? data.toHuman() : `${data}`;336 }337338 static extractData(data: any, type: any): any {339 if(!type) return this.toHuman(data);340 if(['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();341 if(['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();342 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);343 return this.toHuman(data);344 }345346 public static extractEvents(events: { event: any, phase: any }[]): IEvent[] {347 const parsedEvents: IEvent[] = [];348349 events.forEach((record) => {350 const {event, phase} = record;351 const types = event.typeDef;352353 const eventData: IEvent = {354 section: event.section.toString(),355 method: event.method.toString(),356 index: this.extractIndex(event.index),357 data: [],358 phase: phase.toJSON(),359 };360361 event.data.forEach((val: any, index: number) => {362 eventData.data.push(this.extractData(val, types[index]));363 });364365 parsedEvents.push(eventData);366 });367368 return parsedEvents;369 }370}371const InvalidTypeSymbol = Symbol('Invalid type');372373export type Invalid =374 | ((375 invalidType: typeof InvalidTypeSymbol,376 ..._: typeof InvalidTypeSymbol[]377 ) => typeof InvalidTypeSymbol)378 | null379 | undefined;380381type Get2<T, P extends string, E> =382 P extends `${infer Key}.${infer Key2}` ? Key extends keyof T ? Key2 extends keyof T[Key] ? T[Key][Key2] : E : E : E;383type ForceFunction<T> = T extends (...args: any) => any ? T : (...args: any) => Invalid;384385export class ChainHelperBase {386 helperBase: any;387388 transactionStatus = UniqueUtil.transactionStatus;389 chainLogType = UniqueUtil.chainLogType;390 util: typeof UniqueUtil;391 eventHelper: typeof UniqueEventHelper;392 logger: ILogger;393 api: ApiPromise | null;394 forcedNetwork: TNetworks | null;395 network: TNetworks | null;396 wsEndpoint: string | null;397 chainLog: IUniqueHelperLog[];398 children: ChainHelperBase[];399 address: AddressGroup;400 chain: ChainGroup;401402 constructor(logger?: ILogger, helperBase?: any) {403 this.helperBase = helperBase;404405 this.util = UniqueUtil;406 this.eventHelper = UniqueEventHelper;407 if(typeof logger == 'undefined') logger = this.util.getDefaultLogger();408 this.logger = logger;409 this.api = null;410 this.forcedNetwork = null;411 this.network = null;412 this.wsEndpoint = null;413 this.chainLog = [];414 this.children = [];415 this.address = new AddressGroup(this);416 this.chain = new ChainGroup(this);417 }418419 clone(helperCls: ChainHelperBaseConstructor, options: { [key: string]: any } = {}) {420 Object.setPrototypeOf(helperCls.prototype, this);421 const newHelper = new helperCls(this.logger, options);422423 newHelper.api = this.api;424 newHelper.network = this.network;425 newHelper.forceNetwork = this.forceNetwork;426427 this.children.push(newHelper);428429 return newHelper;430 }431432 getEndpoint(): string {433 if(this.wsEndpoint === null) throw Error('No connection was established');434 return this.wsEndpoint;435 }436437 getApi(): ApiPromise {438 if(this.api === null) throw Error('API not initialized');439 return this.api;440 }441442 async subscribeEvents(expectedEvents: { section: string, names: string[] }[]) {443 const collectedEvents: IEvent[] = [];444 const unsubscribe = await this.getApi().query.system.events((events: any) => {445 const ievents = this.eventHelper.extractEvents(events);446 ievents.forEach((event) => {447 expectedEvents.forEach((e => {448 if(event.section === e.section && e.names.includes(event.method)) {449 collectedEvents.push(event);450 }451 }));452 });453 });454 return {unsubscribe: unsubscribe as any, collectedEvents};455 }456457 clearChainLog(): void {458 this.chainLog = [];459 }460461 forceNetwork(value: TNetworks): void {462 this.forcedNetwork = value;463 }464465 async connect(wsEndpoint: string, listeners?: IApiListeners) {466 if(this.api !== null) throw Error('Already connected');467 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);468 this.wsEndpoint = wsEndpoint;469 this.api = api;470 this.network = network;471 }472473 async disconnect() {474 for(const child of this.children) {475 child.clearApi();476 }477478 if(this.api === null) return;479 await this.api.disconnect();480 this.clearApi();481 }482483 clearApi() {484 this.api = null;485 this.network = null;486 }487488 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {489 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;490 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];491492 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;493494 if(['quartz', 'unique', 'sapphire'].indexOf(spec.specName) > -1) return spec.specName;495 return 'opal';496 }497498 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {499 if(!wsEndpoint) throw new Error('wsEndpoint was not set');500 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});501 await api.isReady;502503 const network = await this.detectNetwork(api);504505 await api.disconnect();506507 return network;508 }509510 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{511 api: ApiPromise;512 network: TNetworks;513 }> {514 if(typeof network === 'undefined' || network === null) network = 'opal';515 if(!wsEndpoint) throw new Error('wsEndpoint was not set');516 const supportedRPC = {517 opal: {518 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,519 },520 quartz: {521 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,522 },523 unique: {524 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,525 },526 rococo: {},527 westend: {},528 moonbeam: {},529 moonriver: {},530 acala: {},531 karura: {},532 westmint: {},533 };534 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);535 const rpc = supportedRPC[network] as any;536537 538 539540 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});541542 await api.isReadyOrError;543544 if(typeof listeners === 'undefined') listeners = {};545 for(const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {546 if(!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;547 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);548 }549550 return {api, network};551 }552553 getTransactionStatus(data: { events: { event: IEvent }[], status: any }) {554 const {events, status} = data;555 if(status.isReady) {556 return this.transactionStatus.NOT_READY;557 }558 if(status.isBroadcast) {559 return this.transactionStatus.NOT_READY;560 }561 if(status.isInBlock || status.isFinalized) {562 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');563 if(errors.length > 0) {564 return this.transactionStatus.FAIL;565 }566 if(events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {567 return this.transactionStatus.SUCCESS;568 }569 }570571 return this.transactionStatus.FAIL;572 }573574 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {575 const sign = (callback: any) => {576 if(options !== null) return transaction.signAndSend(sender, options, callback);577 return transaction.signAndSend(sender, callback);578 };579 580 return new Promise(async (resolve, reject) => {581 try {582 const unsub = await sign((result: any) => {583 const status = this.getTransactionStatus(result);584585 if(status === this.transactionStatus.SUCCESS) {586 this.logger.log(`${label} successful`);587 unsub();588 resolve({result, status, blockHash: result.status.asInBlock.toHuman()});589 } else if(status === this.transactionStatus.FAIL) {590 let moduleError = null;591592 if(result.hasOwnProperty('dispatchError')) {593 const dispatchError = result['dispatchError'];594595 if(dispatchError) {596 if(dispatchError.isModule) {597 const modErr = dispatchError.asModule;598 const errorMeta = dispatchError.registry.findMetaError(modErr);599600 moduleError = `${errorMeta.section}.${errorMeta.name}`;601 } else if(dispatchError.isToken) {602 moduleError = `Token: ${dispatchError.asToken}`;603 } else {604 605 moduleError = `Misc: ${dispatchError.toHuman()}`;606 }607 } else {608 this.logger.log(result, this.logger.level.ERROR);609 }610 }611612 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);613 unsub();614 reject({status, moduleError, result});615 }616 });617 } catch (e) {618 this.logger.log(e, this.logger.level.ERROR);619 reject(e);620 }621 });622 }623624 async signTransactionWithoutSending(signer: TSigner, tx: any) {625 const api = this.getApi();626 const signingInfo = await api.derive.tx.signingInfo(signer.address);627628 tx.sign(signer, {629 blockHash: api.genesisHash,630 genesisHash: api.genesisHash,631 runtimeVersion: api.runtimeVersion,632 nonce: signingInfo.nonce,633 });634635 return tx.toHex();636 }637638 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {639 const api = this.getApi();640 const signingInfo = await api.derive.tx.signingInfo(signer.address);641642 643 644 tx.sign(signer, {645 blockHash: api.genesisHash,646 genesisHash: api.genesisHash,647 runtimeVersion: api.runtimeVersion,648 nonce: signingInfo.nonce,649 });650651 if(len === null) {652 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;653 } else {654 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;655 }656 }657658 constructApiCall(apiCall: string, params: any[]) {659 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);660 let call = this.getApi() as any;661 for(const part of apiCall.slice(4).split('.')) {662 call = call[part];663 if(!call) {664 const advice = part.includes('_') ? ' Looks like it needs to be converted to camel case.' : '';665 throw Error(`Function ${part} of api call ${apiCall} not found.${advice}`);666 }667 }668 return call(...params);669 }670671 encodeApiCall(apiCall: string, params: any[]) {672 return this.constructApiCall(apiCall, params).method.toHex();673 }674675 async executeExtrinsic<676 E extends string,677 V extends (678 ...args: any) => any = ForceFunction<679 Get2<680 AugmentedSubmittables<'promise'>,681 E, (...args: any) => Invalid682 >683 >684 >(685 sender: TSigner,686 extrinsic: `api.tx.${E}`,687 params: Parameters<V>,688 expectSuccess = true,689 options: Partial<SignerOptions> | null = null,690 ): Promise<ITransactionResult> {691 if(this.api === null) throw Error('API not initialized');692693 const startTime = (new Date()).getTime();694 let result: ITransactionResult;695 let events: IEvent[] = [];696 try {697 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;698 events = this.eventHelper.extractEvents(result.result.events);699 const errorEvent = events.find((event) => event.method == 'ExecutedFailed' || event.method == 'CreatedFailed');700 if(errorEvent)701 throw Error(errorEvent.method + ': ' + extrinsic);702 }703 catch (e) {704 if(!(e as object).hasOwnProperty('status')) throw e;705 result = e as ITransactionResult;706 }707708 const endTime = (new Date()).getTime();709710 const log = {711 executedAt: endTime,712 executionTime: endTime - startTime,713 type: this.chainLogType.EXTRINSIC,714 status: result.status,715 call: extrinsic,716 signer: this.getSignerAddress(sender),717 params,718 } as IUniqueHelperLog;719720 let errorMessage = '';721722 if(result.status !== this.transactionStatus.SUCCESS) {723 if(result.moduleError) {724 errorMessage = typeof result.moduleError === 'string'725 ? result.moduleError726 : `${Object.keys(result.moduleError)[0]}: ${Object.values(result.moduleError)[0]}`;727 log.moduleError = errorMessage;728 }729 else if(result.result.dispatchError) log.dispatchError = result.result.dispatchError;730 }731 if(events.length > 0) log.events = events;732733 this.chainLog.push(log);734735 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {736 if(result.moduleError) throw Error(`${errorMessage}`);737 else if(result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));738 }739 return result as any;740 }741 executeExtrinsicUncheckedWeight<742 E extends string,743 V extends (744 ...args: any) => any = ForceFunction<745 Get2<746 AugmentedSubmittables<'promise'>,747 E, (...args: any) => Invalid748 >749 >750 >(751 _sender: TSigner,752 _extrinsic: `api.tx.${E}`,753 _params: Parameters<V>,754 _expectSuccess = true,755 _options: Partial<SignerOptions> | null = null,756 ): Promise<ITransactionResult> {757 throw new Error('executeExtrinsicUncheckedWeight only supported in sudo');758 }759760 async callRpc761 762 763 764 765 766 767 768 769 770 771 772 773 (rpc: string, params?: any[]): Promise<any> {774775 if(typeof params === 'undefined') params = [] as any;776 if(this.api === null) throw Error('API not initialized');777 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);778779 const startTime = (new Date()).getTime();780 let result;781 let error = null;782 const log = {783 type: this.chainLogType.RPC,784 call: rpc,785 params,786 } as any as IUniqueHelperLog;787788 try {789 result = await this.constructApiCall(rpc, params as any);790 }791 catch (e) {792 error = e;793 }794795 const endTime = (new Date()).getTime();796797 log.executedAt = endTime;798 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';799 log.executionTime = endTime - startTime;800801 this.chainLog.push(log);802803 if(error !== null) throw error;804805 return result;806 }807808 getSignerAddress(signer: IKeyringPair | string): string {809 if(typeof signer === 'string') return signer;810 return signer.address;811 }812813 fetchAllPalletNames(): string[] {814 if(this.api === null) throw Error('API not initialized');815 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase()).sort();816 }817818 fetchMissingPalletNames(requiredPallets: readonly string[]): string[] {819 const palletNames = this.fetchAllPalletNames();820 return requiredPallets.filter(p => !palletNames.includes(p));821 }822}823824825export class HelperGroup<T extends ChainHelperBase> {826 helper: T;827828 constructor(uniqueHelper: T) {829 this.helper = uniqueHelper;830 }831}832833834class CollectionGroup extends HelperGroup<UniqueHelper> {835 836837838839840841842843844 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {845 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();846 }847848 849850851852853 async getTotalCount(): Promise<number> {854 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();855 }856857 858859860861862863864865866 async getData(collectionId: number): Promise<{867 id: number;868 name: string;869 description: string;870 tokensCount: number;871 admins: CrossAccountId[];872 normalizedOwner: TSubstrateAccount;873 raw: any874 } | null> {875 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);876 const humanCollection = collection.toHuman(), collectionData = {877 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],878 raw: humanCollection,879 } as any, jsonCollection = collection.toJSON();880 if(humanCollection === null) return null;881 collectionData.raw.limits = jsonCollection.limits;882 collectionData.raw.permissions = jsonCollection.permissions;883 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);884 for(const key of ['name', 'description']) {885 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);886 }887888 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))889 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)890 : 0;891 collectionData.admins = await this.getAdmins(collectionId);892893 return collectionData;894 }895896 897898899900901902903904 async getAdmins(collectionId: number, normalize = false): Promise<ICrossAccountId[]> {905 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman() as ICrossAccountId[];906907 return normalize908 ? admins.map(address => CrossAccountId.withNormalizedSubstrate(address))909 : admins;910 }911912 913914915916917918919 async getAllowList(collectionId: number, normalize = false): Promise<ICrossAccountId[]> {920 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman() as ICrossAccountId[];921 return normalize922 ? allowListed.map(address => CrossAccountId.withNormalizedSubstrate(address))923 : allowListed;924 }925926 927928929930931932933 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {934 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();935 }936937 938939940941942943944945 async burn(signer: TSigner, collectionId: number): Promise<boolean> {946 const result = await this.helper.executeExtrinsic(947 signer,948 'api.tx.unique.destroyCollection', [collectionId],949 true,950 );951952 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');953 }954955 956957958959960961962963964 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {965 const result = await this.helper.executeExtrinsic(966 signer,967 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],968 true,969 );970971 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorSet');972 }973974 975976977978979980981982 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {983 const result = await this.helper.executeExtrinsic(984 signer,985 'api.tx.unique.confirmSponsorship', [collectionId],986 true,987 );988989 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'SponsorshipConfirmed');990 }991992 9939949959969979989991000 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {1001 const result = await this.helper.executeExtrinsic(1002 signer,1003 'api.tx.unique.removeCollectionSponsor', [collectionId],1004 true,1005 );10061007 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionSponsorRemoved');1008 }10091010 10111012101310141015101610171018101910201021102210231024102510261027 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {1028 const result = await this.helper.executeExtrinsic(1029 signer,1030 'api.tx.unique.setCollectionLimits', [collectionId, limits],1031 true,1032 );10331034 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionLimitSet');1035 }10361037 103810391040104110421043104410451046 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {1047 const result = await this.helper.executeExtrinsic(1048 signer,1049 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],1050 true,1051 );10521053 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionOwnerChanged');1054 }10551056 105710581059106010611062106310641065 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {1066 const result = await this.helper.executeExtrinsic(1067 signer,1068 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],1069 true,1070 );10711072 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminAdded');1073 }10741075 107610771078107910801081108210831084 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {1085 const result = await this.helper.executeExtrinsic(1086 signer,1087 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],1088 true,1089 );10901091 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionAdminRemoved');1092 }10931094 10951096109710981099110011011102 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {1103 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();1104 }11051106 1107110811091110111111121113 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1114 const result = await this.helper.executeExtrinsic(1115 signer,1116 'api.tx.unique.addToAllowList', [collectionId, addressObj],1117 true,1118 );11191120 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressAdded');1121 }11221123 11241125112611271128112911301131 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1132 const result = await this.helper.executeExtrinsic(1133 signer,1134 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1135 true,1136 );11371138 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'AllowListAddressRemoved');1139 }11401141 114211431144114511461147114811491150 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1151 const result = await this.helper.executeExtrinsic(1152 signer,1153 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1154 true,1155 );11561157 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPermissionSet');1158 }11591160 116111621163116411651166116711681169 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1170 return await this.setPermissions(signer, collectionId, {nesting: permissions});1171 }11721173 11741175117611771178117911801181 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1182 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1183 }11841185 118611871188118911901191119211931194 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1195 const result = await this.helper.executeExtrinsic(1196 signer,1197 'api.tx.unique.setCollectionProperties', [collectionId, properties],1198 true,1199 );12001201 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1202 }12031204 12051206120712081209121012111212 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1213 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1214 }12151216 async getPropertiesConsumedSpace(collectionId: number): Promise<number> {1217 const api = this.helper.getApi();1218 const props = (await api.query.common.collectionProperties(collectionId)).toJSON();12191220 return (props! as any).consumedSpace;1221 }12221223 async getCollectionOptions(collectionId: number) {1224 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1225 }12261227 122812291230123112321233123412351236 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1237 const result = await this.helper.executeExtrinsic(1238 signer,1239 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1240 true,1241 );12421243 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1244 }12451246 12471248124912501251125212531254125512561257 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1258 const result = await this.helper.executeExtrinsic(1259 signer,1260 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1261 true, 1262 );12631264 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1265 }12661267 1268126912701271127212731274127512761277127812791280 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1281 const result = await this.helper.executeExtrinsic(1282 signer,1283 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1284 true, 1285 );1286 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1287 }12881289 12901291129212931294129512961297129812991300 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {1301 const burnResult = await this.helper.executeExtrinsic(1302 signer,1303 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1304 true, 1305 );1306 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1307 if(burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1308 return burnedTokens.success;1309 }13101311 13121313131413151316131713181319132013211322 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1323 const burnResult = await this.helper.executeExtrinsic(1324 signer,1325 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1326 true, 1327 );1328 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1329 return burnedTokens.success && burnedTokens.tokens.length > 0;1330 }13311332 1333133413351336133713381339134013411342 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1343 const approveResult = await this.helper.executeExtrinsic(1344 signer,1345 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1346 true, 1347 );13481349 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1350 }13511352 13531354135513561357135813591360136113621363 async approveTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {1364 const approveResult = await this.helper.executeExtrinsic(1365 signer,1366 'api.tx.unique.approveFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1367 true, 1368 );13691370 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1371 }13721373 1374137513761377137813791380138113821383 async approveTokenFromEth(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1384 const ethMirror = CrossAccountId.fromKeyring(signer).toEthereum().toICrossAccountId();1385 return await this.approveTokenFrom(signer, collectionId, tokenId, ethMirror, toAddressObj, amount);1386 }13871388 1389139013911392139313941395139613971398 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1399 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1400 }14011402 1403140414051406140714081409 async getLastTokenId(collectionId: number): Promise<number> {1410 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1411 }14121413 14141415141614171418141914201421 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1422 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1423 }1424}14251426class NFTnRFT extends CollectionGroup {1427 14281429143014311432143314341435 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1436 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1437 }14381439 1440144114421443144414451446144714481449 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1450 properties: IProperty[];1451 owner: ICrossAccountId;1452 normalizedOwner: ICrossAccountId;1453 } | null> {1454 let args;1455 if(typeof blockHashAt === 'undefined') {1456 args = [collectionId, tokenId];1457 }1458 else {1459 if(propertyKeys.length == 0) {1460 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1461 if(!collection) return null;1462 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1463 }1464 args = [collectionId, tokenId, propertyKeys, blockHashAt];1465 }1466 const tokenData = (await this.helper.callRpc('api.rpc.unique.tokenData', args)).toHuman();1467 if(tokenData === null || tokenData.owner === null) return null;1468 tokenData.normalizedOwner = CrossAccountId.withNormalizedSubstrate(tokenData.owner);1469 return tokenData;1470 }14711472 14731474147514761477147814791480 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId> {1481 let owner;1482 if(typeof blockHashAt === 'undefined') {1483 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1484 } else {1485 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1486 }1487 return CrossAccountId.fromLowerCaseKeys(owner.toJSON()).toICrossAccountId();1488 }14891490 14911492149314941495149614971498 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId | null> {1499 let owner;1500 if(typeof blockHashAt === 'undefined') {1501 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1502 } else {1503 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1504 }15051506 if(owner === null) return null;15071508 return owner.toHuman();1509 }15101511 15121513151415151516151715181519 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1520 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1521 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1522 if(!result) {1523 throw Error('Unable to nest token!');1524 }1525 return result;1526 }15271528 152915301531153215331534153515361537 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1538 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1539 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1540 if(!result) {1541 throw Error('Unable to unnest token!');1542 }1543 return result;1544 }15451546 15471548154915501551155215531554155515561557 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1558 const result = await this.helper.executeExtrinsic(1559 signer,1560 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1561 true,1562 );15631564 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1565 }15661567 15681569157015711572157315741575 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1576 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1577 }15781579 1580158115821583158415851586158715881589 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1590 const result = await this.helper.executeExtrinsic(1591 signer,1592 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1593 true,1594 );15951596 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1597 }15981599 160016011602160316041605160616071608 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1609 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1610 }16111612 161316141615161616171618161916201621 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1622 const result = await this.helper.executeExtrinsic(1623 signer,1624 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1625 true,1626 );16271628 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1629 }16301631 163216331634163516361637163816391640 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1641 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1642 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1643 for(const key of ['name', 'description', 'tokenPrefix']) {1644 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);1645 }16461647 let flags = 0;1648 1649 if(collectionOptions.flags) {1650 for(let i = 0; i < collectionOptions.flags.length; i++){1651 const flag = collectionOptions.flags[i];1652 flags = flags | flag;1653 }1654 }1655 collectionOptions.flags = [flags];16561657 const creationResult = await this.helper.executeExtrinsic(1658 signer,1659 'api.tx.unique.createCollectionEx', [collectionOptions],1660 true, 1661 );1662 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1663 }16641665 getCollectionObject(_collectionId: number): any {1666 return null;1667 }16681669 getTokenObject(_collectionId: number, _tokenId: number): any {1670 return null;1671 }16721673 1674167516761677167816791680 async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1681 return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1682 }16831684 168516861687168816891690 async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1691 const result = await this.helper.executeExtrinsic(1692 signer,1693 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1694 true,1695 );1696 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1697 }1698}169917001701class NFTGroup extends NFTnRFT {1702 170317041705170617071708 override getCollectionObject(collectionId: number): UniqueNFTCollection {1709 return new UniqueNFTCollection(collectionId, this.helper);1710 }17111712 1713171417151716171717181719 override getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1720 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1721 }17221723 1724172517261727172817291730 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1731 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, (await this.getTokenOwner(collectionId, tokenId)))) === 1n;1732 }17331734 1735173617371738173917401741174217431744 override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1745 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1746 }17471748 174917501751175217531754175517561757175817591760 override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1761 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1762 }17631764 17651766176717681769177017711772 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1773 let children;1774 if(typeof blockHashAt === 'undefined') {1775 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1776 } else {1777 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1778 }17791780 return children.toJSON().map((x: any) => ({collectionId: x.collection, tokenId: x.token}));1781 }17821783 178417851786178717881789179017911792179317941795 override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1796 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1797 }17981799 180018011802180318041805 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1806 const creationResult = await this.helper.executeExtrinsic(1807 signer,1808 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1809 NFT: {1810 properties: data.properties,1811 },1812 }],1813 true,1814 );1815 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1816 if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1817 if(createdTokens.tokens.length < 1) throw Error('No tokens minted');1818 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1819 }18201821 182218231824182518261827182818291830183118321833183418351836 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {1837 const creationResult = await this.helper.executeExtrinsic(1838 signer,1839 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1840 true,1841 );1842 const collection = this.getCollectionObject(collectionId);1843 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1844 }18451846 184718481849185018511852185318541855185618571858185918601861186218631864 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { properties?: IProperty[] }[]): Promise<UniqueNFToken[]> {1865 const rawTokens = [];1866 for(const token of tokens) {1867 const raw = {NFT: {properties: token.properties}};1868 rawTokens.push(raw);1869 }1870 const creationResult = await this.helper.executeExtrinsic(1871 signer,1872 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1873 true,1874 );1875 const collection = this.getCollectionObject(collectionId);1876 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1877 }18781879 1880188118821883188418851886188718881889 override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {1890 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1891 }1892}189318941895class RFTGroup extends NFTnRFT {1896 189718981899190019011902 override getCollectionObject(collectionId: number): UniqueRFTCollection {1903 return new UniqueRFTCollection(collectionId, this.helper);1904 }19051906 1907190819091910191119121913 override getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1914 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1915 }19161917 1918191919201921192219231924 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<ICrossAccountId[]> {1925 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map(a => a.toICrossAccountId());1926 }19271928 19291930193119321933193419351936 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1937 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1938 }19391940 1941194219431944194519461947194819491950 override async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1951 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1952 }19531954 19551956195719581959196019611962196319641965 override async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {1966 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1967 }19681969 197019711972197319741975197619771978197919801981 override async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1982 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1983 }19841985 1986198719881989199019911992 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1993 const creationResult = await this.helper.executeExtrinsic(1994 signer,1995 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1996 ReFungible: {1997 pieces: data.pieces,1998 properties: data.properties,1999 },2000 }],2001 true,2002 );2003 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);2004 if(createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');2005 if(createdTokens.tokens.length < 1) throw Error('No tokens minted');2006 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);2007 }20082009 mintMultipleTokens(_signer: TSigner, _collectionId: number, _tokens: { owner: ICrossAccountId, pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {2010 throw Error('Not implemented');2011 2012 2013 2014 2015 2016 2017 2018 }20192020 202120222023202420252026202720282029 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: { pieces: bigint, properties?: IProperty[] }[]): Promise<UniqueRFToken[]> {2030 const rawTokens = [];2031 for(const token of tokens) {2032 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};2033 rawTokens.push(raw);2034 }2035 const creationResult = await this.helper.executeExtrinsic(2036 signer,2037 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2038 true,2039 );2040 const collection = this.getCollectionObject(collectionId);2041 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));2042 }20432044 204520462047204820492050205120522053 override async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount = 1n): Promise<boolean> {2054 return await super.burnToken(signer, collectionId, tokenId, amount);2055 }20562057 2058205920602061206220632064206520662067 override async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {2068 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);2069 }20702071 20722073207420752076207720782079208020812082 override approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {2083 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);2084 }20852086 2087208820892090209120922093 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {2094 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();2095 }20962097 209820992100210121022103210421052106 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {2107 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);2108 const repartitionResult = await this.helper.executeExtrinsic(2109 signer,2110 'api.tx.unique.repartition', [collectionId, tokenId, amount],2111 true,2112 );2113 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');2114 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');2115 }2116}211721182119class FTGroup extends CollectionGroup {2120 212121222123212421252126 getCollectionObject(collectionId: number): UniqueFTCollection {2127 return new UniqueFTCollection(collectionId, this.helper);2128 }21292130 2131213221332134213521362137213821392140214121422143 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {2144 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 2145 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');2146 collectionOptions.mode = {fungible: decimalPoints};2147 for(const key of ['name', 'description', 'tokenPrefix']) {2148 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);2149 }2150 const creationResult = await this.helper.executeExtrinsic(2151 signer,2152 'api.tx.unique.createCollectionEx', [collectionOptions],2153 true,2154 );2155 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));2156 }21572158 215921602161216221632164216521662167 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {2168 const creationResult = await this.helper.executeExtrinsic(2169 signer,2170 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {2171 Fungible: {2172 value: amount,2173 },2174 }],2175 true, 2176 );2177 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2178 }21792180 21812182218321842185218621872188 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: { value: bigint }[], owner: ICrossAccountId): Promise<boolean> {2189 const rawTokens = [];2190 for(const token of tokens) {2191 const raw = {Fungible: {Value: token.value}};2192 rawTokens.push(raw);2193 }2194 const creationResult = await this.helper.executeExtrinsic(2195 signer,2196 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2197 true,2198 );2199 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2200 }22012202 220322042205220622072208 async getTop10Owners(collectionId: number): Promise<ICrossAccountId[]> {2209 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys).map(a => a.toICrossAccountId());2210 }22112212 2213221422152216221722182219 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2220 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2221 }22222223 222422252226222722282229223022312232 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {2233 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2234 }22352236 2237223822392240224122422243224422452246 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {2247 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2248 }22492250 22512252225322542255225622572258 async burnTokens(signer: IKeyringPair, collectionId: number, amount = 1n): Promise<boolean> {2259 return await super.burnToken(signer, collectionId, 0, amount);2260 }22612262 226322642265226622672268226922702271 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount = 1n): Promise<boolean> {2272 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2273 }22742275 22762277227822792280 async getTotalPieces(collectionId: number): Promise<bigint> {2281 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2282 }22832284 2285228622872288228922902291229222932294 approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount = 1n) {2295 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2296 }22972298 2299230023012302230323042305 getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2306 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2307 }2308}230923102311class ChainGroup extends HelperGroup<ChainHelperBase> {2312 23132314231523162317 getChainProperties(): IChainProperties {2318 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2319 return {2320 ss58Format: properties.ss58Format.toJSON(),2321 tokenDecimals: properties.tokenDecimals.toJSON(),2322 tokenSymbol: properties.tokenSymbol.toJSON(),2323 };2324 }23252326 23272328232923302331 async getLatestBlockNumber(): Promise<number> {2332 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2333 }23342335 233623372338233923402341 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2342 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2343 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2344 return blockHash;2345 }23462347 2348 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2349 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2350 if(!blockHash) return null;2351 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2352 }23532354 2355235623572358 async getRelayBlockNumber(): Promise<bigint> {2359 const blockNumber = (await this.helper.callRpc('api.query.parachainSystem.validationData')).toJSON().relayParentNumber;2360 return BigInt(blockNumber);2361 }23622363 236423652366236723682369 async getNonce(address: TSubstrateAccount): Promise<number> {2370 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2371 }2372}23732374export class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2375 237623772378237923802381 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2382 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2383 }23842385 23862387238823892390239123922393 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2394 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);23952396 let transfer = {from: null, to: null, amount: 0n} as any;2397 result.result.events.forEach(({event: {data, method, section}}: any) => {2398 if((section === 'balances') && (method === 'Transfer')) {2399 transfer = {2400 from: this.helper.address.normalizeSubstrate(data[0]),2401 to: this.helper.address.normalizeSubstrate(data[1]),2402 amount: BigInt(data[2]),2403 };2404 }2405 });2406 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2407 && this.helper.address.normalizeSubstrate(address) === transfer.to2408 && BigInt(amount) === transfer.amount;2409 return isSuccess;2410 }24112412 24132414241524162417 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2418 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2419 return {free: accountInfo.free.toBigInt(), frozen: accountInfo.frozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2420 }24212422 2423242424252426 async getTotalIssuance(): Promise<bigint> {2427 const total = (await this.helper.callRpc('api.query.balances.totalIssuance', []));2428 return total.toBigInt();2429 }24302431 async getLocked(address: TSubstrateAccount): Promise<{ id: string, amount: bigint, reason: string }[]> {2432 const locks = (await this.helper.callRpc('api.query.balances.locks', [address])).toHuman();2433 return locks.map((lock: any) => ({id: lock.id, amount: BigInt(lock.amount.replace(/,/g, '')), reasons: lock.reasons}));2434 }2435 async getFrozen(address: TSubstrateAccount): Promise<{ id: string, amount: bigint }[]> {2436 const locks = (await this.helper.api!.query.balances.freezes(address)) as unknown as Array<any>;2437 return locks.map(lock => ({id: lock.id.toUtf8(), amount: lock.amount.toBigInt()}));2438 }2439}24402441export class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2442 244324442445244624472448 async getEthereum(address: TEthereumAccount): Promise<bigint> {2449 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2450 }24512452 24532454245524562457245824592460 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2461 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);24622463 let transfer = {from: null, to: null, amount: 0n} as any;2464 result.result.events.forEach(({event: {data, method, section}}: any) => {2465 if((section === 'balances') && (method === 'Transfer')) {2466 transfer = {2467 from: data[0].toString(),2468 to: data[1].toString(),2469 amount: BigInt(data[2]),2470 };2471 }2472 });2473 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2474 && address === transfer.to2475 && BigInt(amount) === transfer.amount;2476 return isSuccess;2477 }2478}24792480class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2481 subBalanceGroup: SubstrateBalanceGroup<T>;2482 ethBalanceGroup: EthereumBalanceGroup<T>;24832484 constructor(helper: T) {2485 super(helper);2486 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2487 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2488 }24892490 getCollectionCreationPrice(): bigint {2491 return 2n * this.getOneTokenNominal();2492 }2493 24942495249624972498 getOneTokenNominal(): bigint {2499 const chainProperties = this.helper.chain.getChainProperties();2500 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2501 }25022503 250425052506250725082509 getSubstrate(address: TSubstrateAccount): Promise<bigint> {2510 return this.subBalanceGroup.getSubstrate(address);2511 }25122513 25142515251625172518 getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2519 return this.subBalanceGroup.getSubstrateFull(address);2520 }25212522 2523252425252526 getTotalIssuance(): Promise<bigint> {2527 return this.subBalanceGroup.getTotalIssuance();2528 }25292530 253125322533253425352536 getLocked(address: TSubstrateAccount) {2537 return this.subBalanceGroup.getLocked(address);2538 }25392540 25412542254325442545 getFrozen(address: TSubstrateAccount) {2546 return this.subBalanceGroup.getFrozen(address);2547 }25482549 255025512552255325542555 getEthereum(address: TEthereumAccount): Promise<bigint> {2556 return this.ethBalanceGroup.getEthereum(address);2557 }25582559 async setBalanceSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint) {2560 await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceSetBalance', [address, amount], true);2561 }25622563 25642565256625672568256925702571 transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2572 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2573 }25742575 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2576 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);25772578 let transfer = {from: null, to: null, amount: 0n} as any;2579 result.result.events.forEach(({event: {data, method, section}}: any) => {2580 if((section === 'balances') && (method === 'Transfer')) {2581 transfer = {2582 from: this.helper.address.normalizeSubstrate(data[0]),2583 to: this.helper.address.normalizeSubstrate(data[1]),2584 amount: BigInt(data[2]),2585 };2586 }2587 });2588 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2589 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2590 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2591 return isSuccess;2592 }25932594 2595259625972598259926002601 async vestedTransfer(signer: TSigner, address: TSubstrateAccount, schedule: { start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }): Promise<void> {2602 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.vestedTransfer', [address, schedule]);2603 const event = result.result.events2604 .find((e: any) => e.event.section === 'vesting' &&2605 e.event.method === 'VestingScheduleAdded' &&2606 e.event.data[0].toHuman() === signer.address);2607 if(!event) throw Error('Cannot find transfer in events');2608 }26092610 26112612261326142615 async getVestingSchedules(address: TSubstrateAccount): Promise<{ start: bigint, period: bigint, periodCount: bigint, perPeriod: bigint }[]> {2616 const schedule = (await this.helper.callRpc('api.query.vesting.vestingSchedules', [address])).toJSON();2617 return schedule.map((schedule: any) => ({2618 start: BigInt(schedule.start),2619 period: BigInt(schedule.period),2620 periodCount: BigInt(schedule.periodCount),2621 perPeriod: BigInt(schedule.perPeriod),2622 }));2623 }26242625 2626262726282629 async claim(signer: TSigner) {2630 const result = await this.helper.executeExtrinsic(signer, 'api.tx.vesting.claim', []);2631 const event = result.result.events2632 .find((e: any) => e.event.section === 'vesting' &&2633 e.event.method === 'Claimed' &&2634 e.event.data[0].toHuman() === signer.address);2635 if(!event) throw Error('Cannot find claim in events');2636 }2637}26382639class AddressGroup extends HelperGroup<ChainHelperBase> {2640 2641264226432644264526462647 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2648 return CrossAccountId.normalizeSubstrateAddress({Substrate: address}, ss58Format);2649 }26502651 265226532654265526562657 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2658 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2659 }26602661 2662266326642665266626672668 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat = false): TSubstrateAccount {2669 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2670 }26712672 267326742675267626772678 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2679 return CrossAccountId.translateSubToEth(subAddress);2680 }26812682 268326842685268626872688 encodeSubstrateAddress(key: Uint8Array | string | bigint, ss58Format = 42): string {2689 const u8a: Uint8Array = typeof key === 'string'2690 ? hexToU8a(key)2691 : typeof key === 'bigint'2692 ? hexToU8a(key.toString(16))2693 : key;26942695 if(ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2696 throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2697 }26982699 const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2700 if(!allowedDecodedLengths.includes(u8a.length)) {2701 throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2702 }27032704 const u8aPrefix = ss58Format < 642705 ? new Uint8Array([ss58Format])2706 : new Uint8Array([2707 ((ss58Format & 0xfc) >> 2) | 0x40,2708 (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2709 ]);27102711 const input = u8aConcat(u8aPrefix, u8a);27122713 return base58Encode(u8aConcat(2714 input,2715 blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2716 ));2717 }27182719 27202721272227232724 restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2725 if(this.helper.api === null) {2726 throw 'Not connected';2727 }2728 const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2729 if(res === undefined || res === null) {2730 throw 'Restore address error';2731 }2732 return res.toString();2733 }27342735 27362737273827392740 convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2741 if(ethCrossAccount.sub === '0') {2742 return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2743 }27442745 const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2746 return {Substrate: ss58};2747 }27482749 paraSiblingSovereignAccount(paraid: number) {2750 2751 2752 const siblingPrefix = '0x7369626c';27532754 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2755 const suffix = '000000000000000000000000000000000000000000000000';27562757 return siblingPrefix + encodedParaId + suffix;2758 }2759}276027612762class StakingGroup extends HelperGroup<UniqueHelper> {2763 2764276527662767276827692770 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2771 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2772 await this.helper.executeExtrinsic(2773 signer, 'api.tx.appPromotion.stake',2774 [amountToStake], true,2775 );2776 2777 return true;2778 }27792780 2781278227832784278527862787 async unstakeAll(signer: TSigner, label?: string): Promise<string> {2788 if(typeof label === 'undefined') label = `${signer.address}`;2789 const unstakeResult = await this.helper.executeExtrinsic(2790 signer, 'api.tx.appPromotion.unstakeAll',2791 [], true,2792 );2793 return unstakeResult.blockHash;2794 }27952796 2797279827992800280128022803 async unstakePartial(signer: TSigner, amount: bigint, label?: string): Promise<string> {2804 if(typeof label === 'undefined') label = `${signer.address}`;2805 const unstakeResult = await this.helper.executeExtrinsic(2806 signer, 'api.tx.appPromotion.unstakePartial',2807 [amount], true,2808 );2809 return unstakeResult.blockHash;2810 }28112812 28132814281528162817 async getStakesNumber(address: ICrossAccountId): Promise<number> {2818 if('Ethereum' in address) throw Error('only substrate address');2819 return (await this.helper.callRpc('api.query.appPromotion.stakesPerAccount', [address.Substrate])).toNumber();2820 }28212822 28232824282528262827 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2828 if(address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2829 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2830 }28312832 28332834283528362837 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2838 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2839 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => ({2840 block: block.toBigInt(),2841 amount: amount.toBigInt(),2842 }));2843 }28442845 28462847284828492850 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2851 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2852 }28532854 28552856285728582859 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2860 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2861 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => ({2862 block: block.toBigInt(),2863 amount: amount.toBigInt(),2864 }));2865 return result;2866 }2867}286828692870class PreimageGroup extends HelperGroup<UniqueHelper> {2871 async getPreimageInfo(h256: string) {2872 return (await this.helper.callRpc('api.query.preimage.statusFor', [h256])).toJSON();2873 }28742875 287628772878287928802881288228832884 notePreimageFromCall(signer: TSigner, call: any, returnPreimageHash = false) {2885 return this.notePreimage(signer, call.method.toHex(), returnPreimageHash);2886 }28872888 288928902891289228932894289528962897 async notePreimage(signer: TSigner, bytes: string | Uint8Array, returnPreimageHash = false) {2898 const promise = this.helper.executeExtrinsic(signer, 'api.tx.preimage.notePreimage', [bytes]);2899 if(returnPreimageHash) {2900 const result = await promise;2901 const events = result.result.events.filter((x: any) => x.event.method === 'Noted' && x.event.section === 'preimage');2902 const preimageHash = events[0].event.data[0].toHuman();2903 return preimageHash;2904 }2905 return promise;2906 }29072908 290929102911291229132914 unnotePreimage(signer: TSigner, h256: string) {2915 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unnotePreimage', [h256]);2916 }29172918 291929202921292229232924 requestPreimage(signer: TSigner, h256: string) {2925 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.requestPreimage', [h256]);2926 }29272928 292929302931293229332934 unrequestPreimage(signer: TSigner, h256: string) {2935 return this.helper.executeExtrinsic(signer, 'api.tx.preimage.unrequestPreimage', [h256]);2936 }2937}29382939class UtilityGroup<T extends ChainHelperBase> extends HelperGroup<T> {2940 async batch(signer: TSigner, txs: any[]) {2941 return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batch', [txs]);2942 }29432944 async batchAll(signer: TSigner, txs: any[]) {2945 return await this.helper.executeExtrinsic(signer, 'api.tx.utility.batchAll', [txs]);2946 }29472948 batchAllCall(txs: any[]) {2949 return this.helper.constructApiCall('api.tx.utility.batchAll', [txs]);2950 }2951}29522953export type ChainHelperBaseConstructor = new (...args: any[]) => ChainHelperBase;2954export type UniqueHelperConstructor = new (...args: any[]) => UniqueHelper;29552956export class UniqueHelper extends ChainHelperBase {2957 balance: BalanceGroup<UniqueHelper>;2958 collection: CollectionGroup;2959 nft: NFTGroup;2960 rft: RFTGroup;2961 ft: FTGroup;2962 staking: StakingGroup;2963 preimage: PreimageGroup;2964 utility: UtilityGroup<UniqueHelper>;29652966 constructor(logger?: ILogger, options: { [key: string]: any } = {}) {2967 super(logger, options.helperBase ?? UniqueHelper);29682969 this.balance = new BalanceGroup(this);2970 this.collection = new CollectionGroup(this);2971 this.nft = new NFTGroup(this);2972 this.rft = new RFTGroup(this);2973 this.ft = new FTGroup(this);2974 this.staking = new StakingGroup(this);2975 this.preimage = new PreimageGroup(this);2976 this.utility = new UtilityGroup(this);2977 }2978}29792980export class UniqueBaseCollection {2981 helper: UniqueHelper;2982 collectionId: number;29832984 constructor(collectionId: number, uniqueHelper: UniqueHelper) {2985 this.collectionId = collectionId;2986 this.helper = uniqueHelper;2987 }29882989 async getData() {2990 return await this.helper.collection.getData(this.collectionId);2991 }29922993 async getLastTokenId() {2994 return await this.helper.collection.getLastTokenId(this.collectionId);2995 }29962997 async doesTokenExist(tokenId: number) {2998 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);2999 }30003001 async getAdmins() {3002 return await this.helper.collection.getAdmins(this.collectionId);3003 }30043005 async getAllowList() {3006 return await this.helper.collection.getAllowList(this.collectionId);3007 }30083009 async getEffectiveLimits() {3010 return await this.helper.collection.getEffectiveLimits(this.collectionId);3011 }30123013 async getProperties(propertyKeys?: string[] | null) {3014 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);3015 }30163017 async getPropertiesConsumedSpace() {3018 return await this.helper.collection.getPropertiesConsumedSpace(this.collectionId);3019 }30203021 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {3022 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);3023 }30243025 async getOptions() {3026 return await this.helper.collection.getCollectionOptions(this.collectionId);3027 }30283029 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {3030 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);3031 }30323033 async confirmSponsorship(signer: TSigner) {3034 return await this.helper.collection.confirmSponsorship(signer, this.collectionId);3035 }30363037 async removeSponsor(signer: TSigner) {3038 return await this.helper.collection.removeSponsor(signer, this.collectionId);3039 }30403041 async setLimits(signer: TSigner, limits: ICollectionLimits) {3042 return await this.helper.collection.setLimits(signer, this.collectionId, limits);3043 }30443045 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {3046 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);3047 }30483049 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3050 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);3051 }30523053 async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {3054 return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);3055 }30563057 async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {3058 return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);3059 }30603061 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3062 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);3063 }30643065 async setProperties(signer: TSigner, properties: IProperty[]) {3066 return await this.helper.collection.setProperties(signer, this.collectionId, properties);3067 }30683069 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3070 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);3071 }30723073 async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {3074 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);3075 }30763077 async enableNesting(signer: TSigner, permissions: INestingPermissions) {3078 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);3079 }30803081 async disableNesting(signer: TSigner) {3082 return await this.helper.collection.disableNesting(signer, this.collectionId);3083 }30843085 async burn(signer: TSigner) {3086 return await this.helper.collection.burn(signer, this.collectionId);3087 }3088}30893090export class UniqueNFTCollection extends UniqueBaseCollection {3091 getTokenObject(tokenId: number) {3092 return new UniqueNFToken(tokenId, this);3093 }30943095 async getTokensByAddress(addressObj: ICrossAccountId) {3096 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3097 }30983099 async getToken(tokenId: number, blockHashAt?: string) {3100 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3101 }31023103 async getTokenOwner(tokenId: number, blockHashAt?: string) {3104 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3105 }31063107 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3108 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3109 }31103111 async getTokenChildren(tokenId: number, blockHashAt?: string) {3112 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3113 }31143115 async getPropertyPermissions(propertyKeys: string[] | null = null) {3116 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3117 }31183119 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3120 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3121 }31223123 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3124 const api = this.helper.getApi();3125 const props = (await api.query.nonfungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any;31263127 return props?.consumedSpace ?? 0;3128 }31293130 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3131 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3132 }31333134 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3135 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3136 }31373138 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3139 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3140 }31413142 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3143 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3144 }31453146 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3147 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3148 }31493150 async mintMultipleTokens(signer: TSigner, tokens: { owner: ICrossAccountId, properties?: IProperty[] }[]) {3151 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3152 }31533154 async burnToken(signer: TSigner, tokenId: number) {3155 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3156 }31573158 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3159 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3160 }31613162 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3163 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3164 }31653166 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3167 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3168 }31693170 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3171 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3172 }31733174 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3175 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3176 }31773178 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3179 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3180 }3181}31823183export class UniqueRFTCollection extends UniqueBaseCollection {3184 getTokenObject(tokenId: number) {3185 return new UniqueRFToken(tokenId, this);3186 }31873188 async getToken(tokenId: number, blockHashAt?: string) {3189 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3190 }31913192 async getTokenOwner(tokenId: number, blockHashAt?: string) {3193 return await this.helper.rft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3194 }31953196 async getTokensByAddress(addressObj: ICrossAccountId) {3197 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3198 }31993200 async getTop10TokenOwners(tokenId: number) {3201 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3202 }32033204 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3205 return await this.helper.rft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3206 }32073208 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3209 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3210 }32113212 async getTokenTotalPieces(tokenId: number) {3213 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3214 }32153216 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3217 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3218 }32193220 async getPropertyPermissions(propertyKeys: string[] | null = null) {3221 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3222 }32233224 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3225 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3226 }32273228 async getTokenPropertiesConsumedSpace(tokenId: number): Promise<number> {3229 const api = this.helper.getApi();3230 const props = (await api.query.refungible.tokenProperties(this.collectionId, tokenId)).toJSON() as any;32313232 return props?.consumedSpace ?? 0;3233 }32343235 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount = 1n) {3236 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3237 }32383239 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {3240 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3241 }32423243 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount = 1n) {3244 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3245 }32463247 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3248 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3249 }32503251 async mintToken(signer: TSigner, pieces = 1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3252 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3253 }32543255 async mintMultipleTokens(signer: TSigner, tokens: { pieces: bigint, owner: ICrossAccountId, properties?: IProperty[] }[]) {3256 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3257 }32583259 async burnToken(signer: TSigner, tokenId: number, amount = 1n) {3260 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3261 }32623263 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount = 1n) {3264 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3265 }32663267 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3268 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3269 }32703271 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3272 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3273 }32743275 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3276 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3277 }32783279 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3280 return await this.helper.rft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3281 }32823283 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3284 return await this.helper.rft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3285 }3286}32873288export class UniqueFTCollection extends UniqueBaseCollection {3289 async getBalance(addressObj: ICrossAccountId) {3290 return await this.helper.ft.getBalance(this.collectionId, addressObj);3291 }32923293 async getTotalPieces() {3294 return await this.helper.ft.getTotalPieces(this.collectionId);3295 }32963297 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3298 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3299 }33003301 async getTop10Owners() {3302 return await this.helper.ft.getTop10Owners(this.collectionId);3303 }33043305 async mint(signer: TSigner, amount = 1n, owner: ICrossAccountId = {Substrate: signer.address}) {3306 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3307 }33083309 async mintWithOneOwner(signer: TSigner, tokens: { value: bigint }[], owner: ICrossAccountId = {Substrate: signer.address}) {3310 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3311 }33123313 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {3314 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3315 }33163317 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {3318 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3319 }33203321 async burnTokens(signer: TSigner, amount = 1n) {3322 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3323 }33243325 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {3326 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3327 }33283329 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {3330 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3331 }3332}33333334export class UniqueBaseToken {3335 collection: UniqueNFTCollection | UniqueRFTCollection;3336 collectionId: number;3337 tokenId: number;33383339 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3340 this.collection = collection;3341 this.collectionId = collection.collectionId;3342 this.tokenId = tokenId;3343 }33443345 async getNextSponsored(addressObj: ICrossAccountId) {3346 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3347 }33483349 async getProperties(propertyKeys?: string[] | null) {3350 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3351 }33523353 async getTokenPropertiesConsumedSpace() {3354 return await this.collection.getTokenPropertiesConsumedSpace(this.tokenId);3355 }33563357 async setProperties(signer: TSigner, properties: IProperty[]) {3358 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3359 }33603361 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3362 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3363 }33643365 async doesExist() {3366 return await this.collection.doesTokenExist(this.tokenId);3367 }33683369 nestingAccount(): ICrossAccountId {3370 return this.collection.helper.util.getTokenAccount(this);3371 }3372}33733374export class UniqueNFToken extends UniqueBaseToken {3375 declare collection: UniqueNFTCollection;33763377 constructor(tokenId: number, collection: UniqueNFTCollection) {3378 super(tokenId, collection);3379 this.collection = collection;3380 }33813382 async getData(blockHashAt?: string) {3383 return await this.collection.getToken(this.tokenId, blockHashAt);3384 }33853386 async getOwner(blockHashAt?: string) {3387 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3388 }33893390 async getTopmostOwner(blockHashAt?: string) {3391 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3392 }33933394 async getChildren(blockHashAt?: string) {3395 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3396 }33973398 async nest(signer: TSigner, toTokenObj: IToken) {3399 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3400 }34013402 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3403 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3404 }34053406 async transfer(signer: TSigner, addressObj: ICrossAccountId) {3407 return await this.collection.transferToken(signer, this.tokenId, addressObj);3408 }34093410 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3411 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3412 }34133414 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3415 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3416 }34173418 async isApproved(toAddressObj: ICrossAccountId) {3419 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3420 }34213422 async burn(signer: TSigner) {3423 return await this.collection.burnToken(signer, this.tokenId);3424 }34253426 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3427 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3428 }3429}34303431export class UniqueRFToken extends UniqueBaseToken {3432 declare collection: UniqueRFTCollection;34333434 constructor(tokenId: number, collection: UniqueRFTCollection) {3435 super(tokenId, collection);3436 this.collection = collection;3437 }34383439 async getData(blockHashAt?: string) {3440 return await this.collection.getToken(this.tokenId, blockHashAt);3441 }34423443 async getOwner(blockHashAt?: string) {3444 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3445 }34463447 async getTop10Owners() {3448 return await this.collection.getTop10TokenOwners(this.tokenId);3449 }34503451 async getTopmostOwner(blockHashAt?: string) {3452 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3453 }34543455 async nest(signer: TSigner, toTokenObj: IToken) {3456 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3457 }34583459 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3460 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3461 }34623463 async getBalance(addressObj: ICrossAccountId) {3464 return await this.collection.getTokenBalance(this.tokenId, addressObj);3465 }34663467 async getTotalPieces() {3468 return await this.collection.getTokenTotalPieces(this.tokenId);3469 }34703471 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3472 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3473 }34743475 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount = 1n) {3476 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3477 }34783479 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount = 1n) {3480 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3481 }34823483 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount = 1n) {3484 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3485 }34863487 async repartition(signer: TSigner, amount: bigint) {3488 return await this.collection.repartitionToken(signer, this.tokenId, amount);3489 }34903491 async burn(signer: TSigner, amount = 1n) {3492 return await this.collection.burnToken(signer, this.tokenId, amount);3493 }34943495 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount = 1n) {3496 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3497 }3498}