12345678import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents, SignerOptions} from '@polkadot/api/types';10import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm, base58Encode, blake2AsU8a} from '@polkadot/util-crypto';11import {IKeyringPair} from '@polkadot/types/types';12import {hexToU8a} from '@polkadot/util/hex';13import {u8aConcat} from '@polkadot/util/u8a';14import {15 IApiListeners,16 IBlock,17 IEvent,18 IChainProperties,19 ICollectionCreationOptions,20 ICollectionLimits,21 ICollectionPermissions,22 ICrossAccountId,23 ICrossAccountIdLower,24 ILogger,25 INestingPermissions,26 IProperty,27 IStakingInfo,28 ISchedulerOptions,29 ISubstrateBalance,30 IToken,31 ITokenPropertyPermission,32 ITransactionResult,33 IUniqueHelperLog,34 TApiAllowedListeners,35 TEthereumAccount,36 TSigner,37 TSubstrateAccount,38 TNetworks,39 IForeignAssetMetadata,40 AcalaAssetMetadata,41 MoonbeamAssetInfo,42 DemocracyStandardAccountVote,43 IEthCrossAccountId,44} from './types';45import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';4647export class CrossAccountId implements ICrossAccountId {48 Substrate?: TSubstrateAccount;49 Ethereum?: TEthereumAccount;5051 constructor(account: ICrossAccountId) {52 if (account.Substrate) this.Substrate = account.Substrate;53 if (account.Ethereum) this.Ethereum = account.Ethereum;54 }5556 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {57 switch (domain) {58 case 'Substrate': return new CrossAccountId({Substrate: account.address});59 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();60 }61 }6263 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {64 return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});65 }6667 static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {68 return encodeAddress(decodeAddress(address), ss58Format);69 }7071 static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {72 return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});73 }7475 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {76 if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);77 return this;78 }7980 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {81 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));82 }8384 toEthereum(): CrossAccountId {85 if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});86 return this;87 }8889 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {90 return evmToAddress(address, ss58Format);91 }9293 toSubstrate(ss58Format?: number): CrossAccountId {94 if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});95 return this;96 }9798 toLowerCase(): CrossAccountId {99 if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();100 if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();101 return this;102 }103}104105const nesting = {106 toChecksumAddress(address: string): string {107 if (typeof address === 'undefined') return '';108109 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);110111 address = address.toLowerCase().replace(/^0x/i,'');112 const addressHash = keccakAsHex(address).replace(/^0x/i,'');113 const checksumAddress = ['0x'];114115 for (let i = 0; i < address.length; i++) {116 117 if (parseInt(addressHash[i], 16) > 7) {118 checksumAddress.push(address[i].toUpperCase());119 } else {120 checksumAddress.push(address[i]);121 }122 }123 return checksumAddress.join('');124 },125 tokenIdToAddress(collectionId: number, tokenId: number) {126 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);127 },128};129130class UniqueUtil {131 static transactionStatus = {132 NOT_READY: 'NotReady',133 FAIL: 'Fail',134 SUCCESS: 'Success',135 };136137 static chainLogType = {138 EXTRINSIC: 'extrinsic',139 RPC: 'rpc',140 };141142 static getTokenAccount(token: IToken): CrossAccountId {143 return new CrossAccountId({Ethereum: this.getTokenAddress(token)});144 }145146 static getTokenAddress(token: IToken): string {147 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);148 }149150 static getDefaultLogger(): ILogger {151 return {152 log(msg: any, level = 'INFO') {153 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));154 },155 level: {156 ERROR: 'ERROR',157 WARNING: 'WARNING',158 INFO: 'INFO',159 },160 };161 }162163 static vec2str(arr: string[] | number[]) {164 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');165 }166167 static str2vec(string: string) {168 if (typeof string !== 'string') return string;169 return Array.from(string).map(x => x.charCodeAt(0));170 }171172 static fromSeed(seed: string, ss58Format = 42) {173 const keyring = new Keyring({type: 'sr25519', ss58Format});174 return keyring.addFromUri(seed);175 }176177 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {178 if (creationResult.status !== this.transactionStatus.SUCCESS) {179 throw Error('Unable to create collection!');180 }181182 let collectionId = null;183 creationResult.result.events.forEach(({event: {data, method, section}}) => {184 if ((section === 'common') && (method === 'CollectionCreated')) {185 collectionId = parseInt(data[0].toString(), 10);186 }187 });188189 if (collectionId === null) {190 throw Error('No CollectionCreated event was found!');191 }192193 return collectionId;194 }195196 static extractTokensFromCreationResult(creationResult: ITransactionResult): {197 success: boolean,198 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],199 } {200 if (creationResult.status !== this.transactionStatus.SUCCESS) {201 throw Error('Unable to create tokens!');202 }203 let success = false;204 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];205 creationResult.result.events.forEach(({event: {data, method, section}}) => {206 if (method === 'ExtrinsicSuccess') {207 success = true;208 } else if ((section === 'common') && (method === 'ItemCreated')) {209 tokens.push({210 collectionId: parseInt(data[0].toString(), 10),211 tokenId: parseInt(data[1].toString(), 10),212 owner: data[2].toHuman(),213 amount: data[3].toBigInt(),214 });215 }216 });217 return {success, tokens};218 }219220 static extractTokensFromBurnResult(burnResult: ITransactionResult): {221 success: boolean,222 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],223 } {224 if (burnResult.status !== this.transactionStatus.SUCCESS) {225 throw Error('Unable to burn tokens!');226 }227 let success = false;228 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];229 burnResult.result.events.forEach(({event: {data, method, section}}) => {230 if (method === 'ExtrinsicSuccess') {231 success = true;232 } else if ((section === 'common') && (method === 'ItemDestroyed')) {233 tokens.push({234 collectionId: parseInt(data[0].toString(), 10),235 tokenId: parseInt(data[1].toString(), 10),236 owner: data[2].toHuman(),237 amount: data[3].toBigInt(),238 });239 }240 });241 return {success, tokens};242 }243244 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {245 let eventId = null;246 events.forEach(({event: {data, method, section}}) => {247 if ((section === expectedSection) && (method === expectedMethod)) {248 eventId = parseInt(data[0].toString(), 10);249 }250 });251252 if (eventId === null) {253 throw Error(`No ${expectedMethod} event was found!`);254 }255 return eventId === collectionId;256 }257258 static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {259 const normalizeAddress = (address: string | ICrossAccountId) => {260 if(typeof address === 'string') return address;261 const obj = {} as any;262 Object.keys(address).forEach(k => {263 obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];264 });265 if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);266 if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();267 return address;268 };269 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;270 events.forEach(({event: {data, method, section}}) => {271 if ((section === 'common') && (method === 'Transfer')) {272 const hData = (data as any).toJSON();273 transfer = {274 collectionId: hData[0],275 tokenId: hData[1],276 from: normalizeAddress(hData[2]),277 to: normalizeAddress(hData[3]),278 amount: BigInt(hData[4]),279 };280 }281 });282 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;283 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);284 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);285 isSuccess = isSuccess && amount === transfer.amount;286 return isSuccess;287 }288289 static bigIntToDecimals(number: bigint, decimals = 18) {290 const numberStr = number.toString();291 const dotPos = numberStr.length - decimals;292293 if (dotPos <= 0) {294 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;295 } else {296 const intPart = numberStr.substring(0, dotPos);297 const fractPart = numberStr.substring(dotPos);298 return intPart + '.' + fractPart;299 }300 }301}302303class UniqueEventHelper {304 private static extractIndex(index: any): [number, number] | string {305 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];306 return index.toJSON();307 }308309 private static extractSub(data: any, subTypes: any): {[key: string]: any} {310 let obj: any = {};311 let index = 0;312313 if (data.entries) {314 for(const [key, value] of data.entries()) {315 obj[key] = this.extractData(value, subTypes[index]);316 index++;317 }318 } else obj = data.toJSON();319320 return obj;321 }322323 private static toHuman(data: any) {324 return data && data.toHuman ? data.toHuman() : `${data}`;325 }326327 private static extractData(data: any, type: any): any {328 if(!type) return this.toHuman(data);329 if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();330 if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();331 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);332 return this.toHuman(data);333 }334335 public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {336 const parsedEvents: IEvent[] = [];337338 events.forEach((record) => {339 const {event, phase} = record;340 const types = event.typeDef;341342 const eventData: IEvent = {343 section: event.section.toString(),344 method: event.method.toString(),345 index: this.extractIndex(event.index),346 data: [],347 phase: phase.toJSON(),348 };349350 event.data.forEach((val: any, index: number) => {351 eventData.data.push(this.extractData(val, types[index]));352 });353354 parsedEvents.push(eventData);355 });356357 return parsedEvents;358 }359}360361export class ChainHelperBase {362 helperBase: any;363364 transactionStatus = UniqueUtil.transactionStatus;365 chainLogType = UniqueUtil.chainLogType;366 util: typeof UniqueUtil;367 eventHelper: typeof UniqueEventHelper;368 logger: ILogger;369 api: ApiPromise | null;370 forcedNetwork: TNetworks | null;371 network: TNetworks | null;372 chainLog: IUniqueHelperLog[];373 children: ChainHelperBase[];374 address: AddressGroup;375 chain: ChainGroup;376377 constructor(logger?: ILogger, helperBase?: any) {378 this.helperBase = helperBase;379380 this.util = UniqueUtil;381 this.eventHelper = UniqueEventHelper;382 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();383 this.logger = logger;384 this.api = null;385 this.forcedNetwork = null;386 this.network = null;387 this.chainLog = [];388 this.children = [];389 this.address = new AddressGroup(this);390 this.chain = new ChainGroup(this);391 }392393 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {394 Object.setPrototypeOf(helperCls.prototype, this);395 const newHelper = new helperCls(this.logger, options);396397 newHelper.api = this.api;398 newHelper.network = this.network;399 newHelper.forceNetwork = this.forceNetwork;400401 this.children.push(newHelper);402403 return newHelper;404 }405406 getApi(): ApiPromise {407 if(this.api === null) throw Error('API not initialized');408 return this.api;409 }410411 clearChainLog(): void {412 this.chainLog = [];413 }414415 forceNetwork(value: TNetworks): void {416 this.forcedNetwork = value;417 }418419 async connect(wsEndpoint: string, listeners?: IApiListeners) {420 if (this.api !== null) throw Error('Already connected');421 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);422 this.api = api;423 this.network = network;424 }425426 async disconnect() {427 for (const child of this.children) {428 child.clearApi();429 }430431 if (this.api === null) return;432 await this.api.disconnect();433 this.clearApi();434 }435436 clearApi() {437 this.api = null;438 this.network = null;439 }440441 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {442 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;443 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];444445 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;446447 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;448 return 'opal';449 }450451 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {452 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});453 await api.isReady;454455 const network = await this.detectNetwork(api);456457 await api.disconnect();458459 return network;460 }461462 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{463 api: ApiPromise;464 network: TNetworks;465 }> {466 if(typeof network === 'undefined' || network === null) network = 'opal';467 const supportedRPC = {468 opal: {469 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,470 },471 quartz: {472 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,473 },474 unique: {475 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,476 },477 rococo: {},478 westend: {},479 moonbeam: {},480 moonriver: {},481 acala: {},482 karura: {},483 westmint: {},484 };485 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);486 const rpc = supportedRPC[network];487488 489 490491 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});492493 await api.isReadyOrError;494495 if (typeof listeners === 'undefined') listeners = {};496 for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {497 if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;498 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);499 }500501 return {api, network};502 }503504 getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {505 const {events, status} = data;506 if (status.isReady) {507 return this.transactionStatus.NOT_READY;508 }509 if (status.isBroadcast) {510 return this.transactionStatus.NOT_READY;511 }512 if (status.isInBlock || status.isFinalized) {513 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');514 if (errors.length > 0) {515 return this.transactionStatus.FAIL;516 }517 if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {518 return this.transactionStatus.SUCCESS;519 }520 }521522 return this.transactionStatus.FAIL;523 }524525 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {526 const sign = (callback: any) => {527 if(options !== null) return transaction.signAndSend(sender, options, callback);528 return transaction.signAndSend(sender, callback);529 };530 531 return new Promise(async (resolve, reject) => {532 try {533 const unsub = await sign((result: any) => {534 const status = this.getTransactionStatus(result);535536 if (status === this.transactionStatus.SUCCESS) {537 this.logger.log(`${label} successful`);538 unsub();539 resolve({result, status});540 } else if (status === this.transactionStatus.FAIL) {541 let moduleError = null;542543 if (result.hasOwnProperty('dispatchError')) {544 const dispatchError = result['dispatchError'];545546 if (dispatchError) {547 if (dispatchError.isModule) {548 const modErr = dispatchError.asModule;549 const errorMeta = dispatchError.registry.findMetaError(modErr);550551 moduleError = `${errorMeta.section}.${errorMeta.name}`;552 } else {553 moduleError = dispatchError.toHuman();554 }555 } else {556 this.logger.log(result, this.logger.level.ERROR);557 }558 }559560 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);561 unsub();562 reject({status, moduleError, result});563 }564 });565 } catch (e) {566 this.logger.log(e, this.logger.level.ERROR);567 reject(e);568 }569 });570 }571572 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {573 const api = this.getApi();574 const signingInfo = await api.derive.tx.signingInfo(signer.address);575576 577 578 tx.sign(signer, {579 blockHash: api.genesisHash,580 genesisHash: api.genesisHash,581 runtimeVersion: api.runtimeVersion,582 nonce: signingInfo.nonce,583 });584585 if (len === null) {586 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;587 } else {588 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;589 }590 }591592 constructApiCall(apiCall: string, params: any[]) {593 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);594 let call = this.getApi() as any;595 for(const part of apiCall.slice(4).split('.')) {596 call = call[part];597 }598 return call(...params);599 }600601 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null) {602 if(this.api === null) throw Error('API not initialized');603 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);604605 const startTime = (new Date()).getTime();606 let result: ITransactionResult;607 let events: IEvent[] = [];608 try {609 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;610 events = this.eventHelper.extractEvents(result.result.events);611 }612 catch(e) {613 if(!(e as object).hasOwnProperty('status')) throw e;614 result = e as ITransactionResult;615 }616617 const endTime = (new Date()).getTime();618619 const log = {620 executedAt: endTime,621 executionTime: endTime - startTime,622 type: this.chainLogType.EXTRINSIC,623 status: result.status,624 call: extrinsic,625 signer: this.getSignerAddress(sender),626 params,627 } as IUniqueHelperLog;628629 if(result.status !== this.transactionStatus.SUCCESS) {630 if (result.moduleError) log.moduleError = result.moduleError;631 else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;632 }633 if(events.length > 0) log.events = events;634635 this.chainLog.push(log);636637 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {638 if (result.moduleError) throw Error(`${result.moduleError}`);639 else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));640 }641 return result;642 }643644 async callRpc(rpc: string, params?: any[]) {645 if(typeof params === 'undefined') params = [];646 if(this.api === null) throw Error('API not initialized');647 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);648649 const startTime = (new Date()).getTime();650 let result;651 let error = null;652 const log = {653 type: this.chainLogType.RPC,654 call: rpc,655 params,656 } as IUniqueHelperLog;657658 try {659 result = await this.constructApiCall(rpc, params);660 }661 catch(e) {662 error = e;663 }664665 const endTime = (new Date()).getTime();666667 log.executedAt = endTime;668 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';669 log.executionTime = endTime - startTime;670671 this.chainLog.push(log);672673 if(error !== null) throw error;674675 return result;676 }677678 getSignerAddress(signer: IKeyringPair | string): string {679 if(typeof signer === 'string') return signer;680 return signer.address;681 }682683 fetchAllPalletNames(): string[] {684 if(this.api === null) throw Error('API not initialized');685 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());686 }687688 fetchMissingPalletNames(requiredPallets: string[]): string[] {689 const palletNames = this.fetchAllPalletNames();690 return requiredPallets.filter(p => !palletNames.includes(p));691 }692}693694695class HelperGroup<T extends ChainHelperBase> {696 helper: T;697698 constructor(uniqueHelper: T) {699 this.helper = uniqueHelper;700 }701}702703704class CollectionGroup extends HelperGroup<UniqueHelper> {705 706707708709710711712713714 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {715 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();716 }717718 719720721722723 async getTotalCount(): Promise<number> {724 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();725 }726727 728729730731732733734735736 async getData(collectionId: number): Promise<{737 id: number;738 name: string;739 description: string;740 tokensCount: number;741 admins: CrossAccountId[];742 normalizedOwner: TSubstrateAccount;743 raw: any744 } | null> {745 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);746 const humanCollection = collection.toHuman(), collectionData = {747 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],748 raw: humanCollection,749 } as any, jsonCollection = collection.toJSON();750 if (humanCollection === null) return null;751 collectionData.raw.limits = jsonCollection.limits;752 collectionData.raw.permissions = jsonCollection.permissions;753 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);754 for (const key of ['name', 'description']) {755 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);756 }757758 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))759 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)760 : 0;761 collectionData.admins = await this.getAdmins(collectionId);762763 return collectionData;764 }765766 767768769770771772773774 async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {775 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();776777 return normalize778 ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())779 : admins;780 }781782 783784785786787788789 async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {790 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();791 return normalize792 ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())793 : allowListed;794 }795796 797798799800801802803 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {804 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();805 }806807 808809810811812813814815 async burn(signer: TSigner, collectionId: number): Promise<boolean> {816 const result = await this.helper.executeExtrinsic(817 signer,818 'api.tx.unique.destroyCollection', [collectionId],819 true,820 );821822 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');823 }824825 826827828829830831832833834 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {835 const result = await this.helper.executeExtrinsic(836 signer,837 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],838 true,839 );840841 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet');842 }843844 845846847848849850851852 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {853 const result = await this.helper.executeExtrinsic(854 signer,855 'api.tx.unique.confirmSponsorship', [collectionId],856 true,857 );858859 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed');860 }861862 863864865866867868869870 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {871 const result = await this.helper.executeExtrinsic(872 signer,873 'api.tx.unique.removeCollectionSponsor', [collectionId],874 true,875 );876877 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorRemoved');878 }879880 881882883884885886887888889890891892893894895896897 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {898 const result = await this.helper.executeExtrinsic(899 signer,900 'api.tx.unique.setCollectionLimits', [collectionId, limits],901 true,902 );903904 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet');905 }906907 908909910911912913914915916 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {917 const result = await this.helper.executeExtrinsic(918 signer,919 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],920 true,921 );922923 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged');924 }925926 927928929930931932933934935 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {936 const result = await this.helper.executeExtrinsic(937 signer,938 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],939 true,940 );941942 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded');943 }944945 946947948949950951952953954 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {955 const result = await this.helper.executeExtrinsic(956 signer,957 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],958 true,959 );960961 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved');962 }963964 965966967968969970971972 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {973 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();974 }975976 977978979980981982983 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {984 const result = await this.helper.executeExtrinsic(985 signer,986 'api.tx.unique.addToAllowList', [collectionId, addressObj],987 true,988 );989990 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressAdded');991 }992993 99499599699799899910001001 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {1002 const result = await this.helper.executeExtrinsic(1003 signer,1004 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1005 true,1006 );10071008 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved');1009 }10101011 101210131014101510161017101810191020 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1021 const result = await this.helper.executeExtrinsic(1022 signer,1023 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1024 true,1025 );10261027 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet');1028 }10291030 103110321033103410351036103710381039 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1040 return await this.setPermissions(signer, collectionId, {nesting: permissions});1041 }10421043 10441045104610471048104910501051 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1052 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1053 }10541055 105610571058105910601061106210631064 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1065 const result = await this.helper.executeExtrinsic(1066 signer,1067 'api.tx.unique.setCollectionProperties', [collectionId, properties],1068 true,1069 );10701071 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1072 }10731074 10751076107710781079108010811082 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1083 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1084 }10851086 async getCollectionOptions(collectionId: number) {1087 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1088 }10891090 109110921093109410951096109710981099 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1100 const result = await this.helper.executeExtrinsic(1101 signer,1102 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1103 true,1104 );11051106 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1107 }11081109 11101111111211131114111511161117111811191120 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1121 const result = await this.helper.executeExtrinsic(1122 signer,1123 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1124 true, 1125 );11261127 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1128 }11291130 1131113211331134113511361137113811391140114111421143 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1144 const result = await this.helper.executeExtrinsic(1145 signer,1146 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1147 true, 1148 );1149 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1150 }11511152 11531154115511561157115811591160116111621163 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1164 const burnResult = await this.helper.executeExtrinsic(1165 signer,1166 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1167 true, 1168 );1169 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1170 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1171 return burnedTokens.success;1172 }11731174 11751176117711781179118011811182118311841185 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1186 const burnResult = await this.helper.executeExtrinsic(1187 signer,1188 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1189 true, 1190 );1191 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1192 return burnedTokens.success && burnedTokens.tokens.length > 0;1193 }11941195 1196119711981199120012011202120312041205 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1206 const approveResult = await this.helper.executeExtrinsic(1207 signer,1208 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1209 true, 1210 );12111212 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1213 }12141215 1216121712181219122012211222122312241225 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1226 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1227 }12281229 1230123112321233123412351236 async getLastTokenId(collectionId: number): Promise<number> {1237 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1238 }12391240 12411242124312441245124612471248 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1249 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1250 }1251}12521253class NFTnRFT extends CollectionGroup {1254 12551256125712581259126012611262 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1263 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1264 }12651266 1267126812691270127112721273127412751276 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1277 properties: IProperty[];1278 owner: CrossAccountId;1279 normalizedOwner: CrossAccountId;1280 }| null> {1281 let tokenData;1282 if(typeof blockHashAt === 'undefined') {1283 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1284 }1285 else {1286 if(propertyKeys.length == 0) {1287 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1288 if(!collection) return null;1289 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1290 }1291 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1292 }1293 tokenData = tokenData.toHuman();1294 if (tokenData === null || tokenData.owner === null) return null;1295 const owner = {} as any;1296 for (const key of Object.keys(tokenData.owner)) {1297 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1298 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1299 : tokenData.owner[key];1300 }1301 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1302 return tokenData;1303 }13041305 13061307130813091310131113121313131413151316 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1317 const result = await this.helper.executeExtrinsic(1318 signer,1319 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1320 true,1321 );13221323 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1324 }13251326 13271328132913301331133213331334 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1335 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1336 }13371338 1339134013411342134313441345134613471348 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1349 const result = await this.helper.executeExtrinsic(1350 signer,1351 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1352 true,1353 );13541355 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1356 }13571358 135913601361136213631364136513661367 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1368 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1369 }13701371 137213731374137513761377137813791380 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1381 const result = await this.helper.executeExtrinsic(1382 signer,1383 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1384 true,1385 );13861387 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1388 }13891390 139113921393139413951396139713981399 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1400 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1401 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1402 for (const key of ['name', 'description', 'tokenPrefix']) {1403 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);1404 }1405 const creationResult = await this.helper.executeExtrinsic(1406 signer,1407 'api.tx.unique.createCollectionEx', [collectionOptions],1408 true, 1409 );1410 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1411 }14121413 getCollectionObject(_collectionId: number): any {1414 return null;1415 }14161417 getTokenObject(_collectionId: number, _tokenId: number): any {1418 return null;1419 }14201421 1422142314241425142614271428 async allowanceForAll(collectionId: number, owner: ICrossAccountId, operator: ICrossAccountId): Promise<boolean> {1429 return (await this.helper.callRpc('api.rpc.unique.allowanceForAll', [collectionId, owner, operator])).toJSON();1430 }14311432 143314341435143614371438 async setAllowanceForAll(signer: TSigner, collectionId: number, operator: ICrossAccountId, approved: boolean): Promise<boolean> {1439 const result = await this.helper.executeExtrinsic(1440 signer,1441 'api.tx.unique.setAllowanceForAll', [collectionId, operator, approved],1442 true,1443 );1444 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'ApprovedForAll');1445 }1446}144714481449class NFTGroup extends NFTnRFT {1450 145114521453145414551456 getCollectionObject(collectionId: number): UniqueNFTCollection {1457 return new UniqueNFTCollection(collectionId, this.helper);1458 }14591460 1461146214631464146514661467 getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1468 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1469 }14701471 14721473147414751476147714781479 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1480 let owner;1481 if (typeof blockHashAt === 'undefined') {1482 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1483 } else {1484 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1485 }1486 return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1487 }14881489 1490149114921493149414951496 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1497 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1498 }14991500 1501150215031504150515061507150815091510 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1511 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1512 }15131514 151515161517151815191520152115221523152415251526 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1527 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1528 }15291530 15311532153315341535153615371538 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1539 let owner;1540 if (typeof blockHashAt === 'undefined') {1541 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1542 } else {1543 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1544 }15451546 if (owner === null) return null;15471548 return owner.toHuman();1549 }15501551 15521553155415551556155715581559 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1560 let children;1561 if(typeof blockHashAt === 'undefined') {1562 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1563 } else {1564 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1565 }15661567 return children.toJSON().map((x: any) => {1568 return {collectionId: x.collection, tokenId: x.token};1569 });1570 }15711572 15731574157515761577157815791580 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1581 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1582 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1583 if(!result) {1584 throw Error('Unable to nest token!');1585 }1586 return result;1587 }15881589 159015911592159315941595159615971598 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1599 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1600 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1601 if(!result) {1602 throw Error('Unable to unnest token!');1603 }1604 return result;1605 }16061607 160816091610161116121613161416151616161716181619 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1620 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1621 }16221623 162416251626162716281629 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1630 const creationResult = await this.helper.executeExtrinsic(1631 signer,1632 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1633 nft: {1634 properties: data.properties,1635 },1636 }],1637 true,1638 );1639 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1640 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1641 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1642 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1643 }16441645 164616471648164916501651165216531654165516561657165816591660 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1661 const creationResult = await this.helper.executeExtrinsic(1662 signer,1663 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1664 true,1665 );1666 const collection = this.getCollectionObject(collectionId);1667 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1668 }16691670 167116721673167416751676167716781679168016811682168316841685168616871688 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1689 const rawTokens = [];1690 for (const token of tokens) {1691 const raw = {NFT: {properties: token.properties}};1692 rawTokens.push(raw);1693 }1694 const creationResult = await this.helper.executeExtrinsic(1695 signer,1696 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1697 true,1698 );1699 const collection = this.getCollectionObject(collectionId);1700 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1701 }17021703 1704170517061707170817091710171117121713 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1714 return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1715 }1716}171717181719class RFTGroup extends NFTnRFT {1720 172117221723172417251726 getCollectionObject(collectionId: number): UniqueRFTCollection {1727 return new UniqueRFTCollection(collectionId, this.helper);1728 }17291730 1731173217331734173517361737 getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1738 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1739 }17401741 1742174317441745174617471748 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1749 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1750 }17511752 17531754175517561757175817591760 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1761 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1762 }17631764 1765176617671768176917701771177217731774 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1775 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1776 }17771778 17791780178117821783178417851786178717881789 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1790 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1791 }17921793 179417951796179717981799180018011802180318041805 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1806 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1807 }18081809 1810181118121813181418151816 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1817 const creationResult = await this.helper.executeExtrinsic(1818 signer,1819 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1820 refungible: {1821 pieces: data.pieces,1822 properties: data.properties,1823 },1824 }],1825 true,1826 );1827 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1828 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1829 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1830 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1831 }18321833 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1834 throw Error('Not implemented');1835 const creationResult = await this.helper.executeExtrinsic(1836 signer,1837 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1838 true, 1839 );1840 const collection = this.getCollectionObject(collectionId);1841 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1842 }18431844 184518461847184818491850185118521853 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1854 const rawTokens = [];1855 for (const token of tokens) {1856 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1857 rawTokens.push(raw);1858 }1859 const creationResult = await this.helper.executeExtrinsic(1860 signer,1861 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1862 true,1863 );1864 const collection = this.getCollectionObject(collectionId);1865 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1866 }18671868 186918701871187218731874187518761877 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1878 return await super.burnToken(signer, collectionId, tokenId, amount);1879 }18801881 1882188318841885188618871888188918901891 async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1892 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1893 }18941895 18961897189818991900190119021903190419051906 approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1907 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1908 }19091910 1911191219131914191519161917 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1918 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1919 }19201921 192219231924192519261927192819291930 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1931 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1932 const repartitionResult = await this.helper.executeExtrinsic(1933 signer,1934 'api.tx.unique.repartition', [collectionId, tokenId, amount],1935 true,1936 );1937 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1938 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1939 }1940}194119421943class FTGroup extends CollectionGroup {1944 194519461947194819491950 getCollectionObject(collectionId: number): UniqueFTCollection {1951 return new UniqueFTCollection(collectionId, this.helper);1952 }19531954 1955195619571958195919601961196219631964196519661967 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1968 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1969 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1970 collectionOptions.mode = {fungible: decimalPoints};1971 for (const key of ['name', 'description', 'tokenPrefix']) {1972 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);1973 }1974 const creationResult = await this.helper.executeExtrinsic(1975 signer,1976 'api.tx.unique.createCollectionEx', [collectionOptions],1977 true,1978 );1979 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1980 }19811982 198319841985198619871988198919901991 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {1992 const creationResult = await this.helper.executeExtrinsic(1993 signer,1994 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {1995 fungible: {1996 value: amount,1997 },1998 }],1999 true, 2000 );2001 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2002 }20032004 20052006200720082009201020112012 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {2013 const rawTokens = [];2014 for (const token of tokens) {2015 const raw = {Fungible: {Value: token.value}};2016 rawTokens.push(raw);2017 }2018 const creationResult = await this.helper.executeExtrinsic(2019 signer,2020 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],2021 true,2022 );2023 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');2024 }20252026 202720282029203020312032 async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2033 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2034 }20352036 2037203820392040204120422043 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2044 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2045 }20462047 204820492050205120522053205420552056 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2057 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2058 }20592060 2061206220632064206520662067206820692070 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2071 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2072 }20732074 20752076207720782079208020812082 async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2083 return await super.burnToken(signer, collectionId, 0, amount);2084 }20852086 208720882089209020912092209320942095 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2096 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2097 }20982099 21002101210221032104 async getTotalPieces(collectionId: number): Promise<bigint> {2105 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2106 }21072108 2109211021112112211321142115211621172118 approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2119 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2120 }21212122 2123212421252126212721282129 getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2130 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2131 }2132}213321342135class ChainGroup extends HelperGroup<ChainHelperBase> {2136 21372138213921402141 getChainProperties(): IChainProperties {2142 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2143 return {2144 ss58Format: properties.ss58Format.toJSON(),2145 tokenDecimals: properties.tokenDecimals.toJSON(),2146 tokenSymbol: properties.tokenSymbol.toJSON(),2147 };2148 }21492150 21512152215321542155 async getLatestBlockNumber(): Promise<number> {2156 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2157 }21582159 216021612162216321642165 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2166 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2167 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2168 return blockHash;2169 }21702171 2172 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2173 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2174 if (!blockHash) return null;2175 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2176 }21772178 217921802181218221832184 async getNonce(address: TSubstrateAccount): Promise<number> {2185 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2186 }2187}21882189class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2190 219121922193219421952196 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2197 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2198 }21992200 22012202220322042205220622072208 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2209 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22102211 let transfer = {from: null, to: null, amount: 0n} as any;2212 result.result.events.forEach(({event: {data, method, section}}) => {2213 if ((section === 'balances') && (method === 'Transfer')) {2214 transfer = {2215 from: this.helper.address.normalizeSubstrate(data[0]),2216 to: this.helper.address.normalizeSubstrate(data[1]),2217 amount: BigInt(data[2]),2218 };2219 }2220 });2221 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2222 && this.helper.address.normalizeSubstrate(address) === transfer.to2223 && BigInt(amount) === transfer.amount;2224 return isSuccess;2225 }22262227 22282229223022312232 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2233 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2234 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2235 }2236}22372238class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2239 224022412242224322442245 async getEthereum(address: TEthereumAccount): Promise<bigint> {2246 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2247 }22482249 22502251225222532254225522562257 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2258 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22592260 let transfer = {from: null, to: null, amount: 0n} as any;2261 result.result.events.forEach(({event: {data, method, section}}) => {2262 if ((section === 'balances') && (method === 'Transfer')) {2263 transfer = {2264 from: data[0].toString(),2265 to: data[1].toString(),2266 amount: BigInt(data[2]),2267 };2268 }2269 });2270 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2271 && address === transfer.to2272 && BigInt(amount) === transfer.amount;2273 return isSuccess;2274 }2275}22762277class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2278 subBalanceGroup: SubstrateBalanceGroup<T>;2279 ethBalanceGroup: EthereumBalanceGroup<T>;22802281 constructor(helper: T) {2282 super(helper);2283 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2284 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2285 }22862287 getCollectionCreationPrice(): bigint {2288 return 2n * this.getOneTokenNominal();2289 }2290 22912292229322942295 getOneTokenNominal(): bigint {2296 const chainProperties = this.helper.chain.getChainProperties();2297 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2298 }22992300 230123022303230423052306 getSubstrate(address: TSubstrateAccount): Promise<bigint> {2307 return this.subBalanceGroup.getSubstrate(address);2308 }23092310 23112312231323142315 getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2316 return this.subBalanceGroup.getSubstrateFull(address);2317 }23182319 232023212322232323242325 getEthereum(address: TEthereumAccount): Promise<bigint> {2326 return this.ethBalanceGroup.getEthereum(address);2327 }23282329 23302331233223332334233523362337 transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2338 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2339 }23402341 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2342 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);23432344 let transfer = {from: null, to: null, amount: 0n} as any;2345 result.result.events.forEach(({event: {data, method, section}}) => {2346 if ((section === 'balances') && (method === 'Transfer')) {2347 transfer = {2348 from: this.helper.address.normalizeSubstrate(data[0]),2349 to: this.helper.address.normalizeSubstrate(data[1]),2350 amount: BigInt(data[2]),2351 };2352 }2353 });2354 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2355 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2356 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2357 return isSuccess;2358 }2359}23602361class AddressGroup extends HelperGroup<ChainHelperBase> {2362 2363236423652366236723682369 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2370 return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2371 }23722373 237423752376237723782379 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2380 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2381 }23822383 2384238523862387238823892390 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2391 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2392 }23932394 239523962397239823992400 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2401 return CrossAccountId.translateSubToEth(subAddress);2402 }24032404 240524062407240824092410 encodeSubstrateAddress (key: Uint8Array | string | bigint, ss58Format = 42): string {2411 const u8a :Uint8Array = typeof key === 'string'2412 ? hexToU8a(key)2413 : typeof key === 'bigint'2414 ? hexToU8a(key.toString(16))2415 : key;2416 2417 if (ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2418 throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2419 }2420 2421 const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2422 if (!allowedDecodedLengths.includes(u8a.length)) {2423 throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2424 }2425 2426 const u8aPrefix = ss58Format < 642427 ? new Uint8Array([ss58Format])2428 : new Uint8Array([2429 ((ss58Format & 0xfc) >> 2) | 0x40,2430 (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2431 ]);24322433 const input = u8aConcat(u8aPrefix, u8a);2434 2435 return base58Encode(u8aConcat(2436 input,2437 blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2438 ));2439 }24402441 24422443244424452446 restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2447 if (this.helper.api === null) {2448 throw 'Not connected';2449 }2450 const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2451 if (res === undefined || res === null) {2452 throw 'Restore address error';2453 }2454 return res.toString();2455 }24562457 24582459246024612462 convertCrossAccountFromEthCrossAccount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2463 if (ethCrossAccount.sub === '0') {2464 return {Ethereum: ethCrossAccount.eth.toLocaleLowerCase()};2465 }2466 2467 const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.sub));2468 return {Substrate: ss58};2469 }24702471 paraSiblingSovereignAccount(paraid: number) {2472 2473 2474 const siblingPrefix = '0x7369626c';24752476 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2477 const suffix = '000000000000000000000000000000000000000000000000';24782479 return siblingPrefix + encodedParaId + suffix;2480 }2481}24822483class StakingGroup extends HelperGroup<UniqueHelper> {2484 2485248624872488248924902491 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2492 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2493 const _stakeResult = await this.helper.executeExtrinsic(2494 signer, 'api.tx.appPromotion.stake',2495 [amountToStake], true,2496 );2497 2498 return true;2499 }25002501 2502250325042505250625072508 async unstake(signer: TSigner, label?: string): Promise<number> {2509 if(typeof label === 'undefined') label = `${signer.address}`;2510 const _unstakeResult = await this.helper.executeExtrinsic(2511 signer, 'api.tx.appPromotion.unstake',2512 [], true,2513 );2514 2515 return 1;2516 }25172518 25192520252125222523 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2524 if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2525 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2526 }25272528 25292530253125322533 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2534 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2535 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2536 return {2537 block: block.toBigInt(),2538 amount: amount.toBigInt(),2539 };2540 });2541 }25422543 25442545254625472548 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2549 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2550 }25512552 25532554255525562557 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2558 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2559 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2560 return {2561 block: block.toBigInt(),2562 amount: amount.toBigInt(),2563 };2564 });2565 return result;2566 }2567}25682569class SchedulerGroup extends HelperGroup<UniqueHelper> {2570 constructor(helper: UniqueHelper) {2571 super(helper);2572 }25732574 cancelScheduled(signer: TSigner, scheduledId: string) {2575 return this.helper.executeExtrinsic(2576 signer,2577 'api.tx.scheduler.cancelNamed',2578 [scheduledId],2579 true,2580 );2581 }25822583 changePriority(signer: TSigner, scheduledId: string, priority: number) {2584 return this.helper.executeExtrinsic(2585 signer,2586 'api.tx.scheduler.changeNamedPriority',2587 [scheduledId, priority],2588 true,2589 );2590 }25912592 scheduleAt<T extends UniqueHelper>(2593 executionBlockNumber: number,2594 options: ISchedulerOptions = {},2595 ) {2596 return this.schedule<T>('schedule', executionBlockNumber, options);2597 }25982599 scheduleAfter<T extends UniqueHelper>(2600 blocksBeforeExecution: number,2601 options: ISchedulerOptions = {},2602 ) {2603 return this.schedule<T>('scheduleAfter', blocksBeforeExecution, options);2604 }26052606 schedule<T extends UniqueHelper>(2607 scheduleFn: 'schedule' | 'scheduleAfter',2608 blocksNum: number,2609 options: ISchedulerOptions = {},2610 ) {2611 2612 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2613 return this.helper.clone(ScheduledHelperType, {2614 scheduleFn,2615 blocksNum,2616 options,2617 }) as T;2618 }2619}26202621class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2622 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2623 await this.helper.executeExtrinsic(2624 signer,2625 'api.tx.foreignAssets.registerForeignAsset',2626 [ownerAddress, location, metadata],2627 true,2628 );2629 }26302631 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2632 await this.helper.executeExtrinsic(2633 signer,2634 'api.tx.foreignAssets.updateForeignAsset',2635 [foreignAssetId, location, metadata],2636 true,2637 );2638 }2639}26402641class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2642 palletName: string;26432644 constructor(helper: T, palletName: string) {2645 super(helper);26462647 this.palletName = palletName;2648 }26492650 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2651 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2652 }2653}26542655class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2656 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2657 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2658 }26592660 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2661 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2662 }26632664 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2665 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2666 }2667}26682669class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2670 async accounts(address: string, currencyId: any) {2671 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2672 return BigInt(free);2673 }2674}26752676class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2677 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2678 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2679 }26802681 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2682 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2683 }26842685 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2686 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2687 }26882689 async account(assetId: string | number, address: string) {2690 const accountAsset = (2691 await this.helper.callRpc('api.query.assets.account', [assetId, address])2692 ).toJSON()! as any;26932694 if (accountAsset !== null) {2695 return BigInt(accountAsset['balance']);2696 } else {2697 return null;2698 }2699 }2700}27012702class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2703 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2704 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2705 }2706}27072708class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2709 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2710 const apiPrefix = 'api.tx.assetManager.';27112712 const registerTx = this.helper.constructApiCall(2713 apiPrefix + 'registerForeignAsset',2714 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2715 );27162717 const setUnitsTx = this.helper.constructApiCall(2718 apiPrefix + 'setAssetUnitsPerSecond',2719 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2720 );27212722 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2723 const encodedProposal = batchCall?.method.toHex() || '';2724 return encodedProposal;2725 }27262727 async assetTypeId(location: any) {2728 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2729 }2730}27312732class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2733 async notePreimage(signer: TSigner, encodedProposal: string) {2734 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2735 }27362737 externalProposeMajority(proposalHash: string) {2738 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2739 }27402741 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2742 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2743 }27442745 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2746 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2747 }2748}27492750class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2751 collective: string;27522753 constructor(helper: MoonbeamHelper, collective: string) {2754 super(helper);27552756 this.collective = collective;2757 }27582759 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2760 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2761 }27622763 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2764 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2765 }27662767 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2768 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2769 }27702771 async proposalCount() {2772 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2773 }2774}27752776export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2777export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;27782779export class UniqueHelper extends ChainHelperBase {2780 balance: BalanceGroup<UniqueHelper>;2781 collection: CollectionGroup;2782 nft: NFTGroup;2783 rft: RFTGroup;2784 ft: FTGroup;2785 staking: StakingGroup;2786 scheduler: SchedulerGroup;2787 foreignAssets: ForeignAssetsGroup;2788 xcm: XcmGroup<UniqueHelper>;2789 xTokens: XTokensGroup<UniqueHelper>;2790 tokens: TokensGroup<UniqueHelper>;27912792 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2793 super(logger, options.helperBase ?? UniqueHelper);27942795 this.balance = new BalanceGroup(this);2796 this.collection = new CollectionGroup(this);2797 this.nft = new NFTGroup(this);2798 this.rft = new RFTGroup(this);2799 this.ft = new FTGroup(this);2800 this.staking = new StakingGroup(this);2801 this.scheduler = new SchedulerGroup(this);2802 this.foreignAssets = new ForeignAssetsGroup(this);2803 this.xcm = new XcmGroup(this, 'polkadotXcm');2804 this.xTokens = new XTokensGroup(this);2805 this.tokens = new TokensGroup(this);2806 }28072808 getSudo<T extends UniqueHelper>() {2809 2810 const SudoHelperType = SudoHelper(this.helperBase);2811 return this.clone(SudoHelperType) as T;2812 }2813}28142815export class XcmChainHelper extends ChainHelperBase {2816 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2817 const wsProvider = new WsProvider(wsEndpoint);2818 this.api = new ApiPromise({2819 provider: wsProvider,2820 });2821 await this.api.isReadyOrError;2822 this.network = await UniqueHelper.detectNetwork(this.api);2823 }2824}28252826export class RelayHelper extends XcmChainHelper {2827 xcm: XcmGroup<RelayHelper>;28282829 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2830 super(logger, options.helperBase ?? RelayHelper);28312832 this.xcm = new XcmGroup(this, 'xcmPallet');2833 }2834}28352836export class WestmintHelper extends XcmChainHelper {2837 balance: SubstrateBalanceGroup<WestmintHelper>;2838 xcm: XcmGroup<WestmintHelper>;2839 assets: AssetsGroup<WestmintHelper>;2840 xTokens: XTokensGroup<WestmintHelper>;28412842 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2843 super(logger, options.helperBase ?? WestmintHelper);28442845 this.balance = new SubstrateBalanceGroup(this);2846 this.xcm = new XcmGroup(this, 'polkadotXcm');2847 this.assets = new AssetsGroup(this);2848 this.xTokens = new XTokensGroup(this);2849 }2850}28512852export class MoonbeamHelper extends XcmChainHelper {2853 balance: EthereumBalanceGroup<MoonbeamHelper>;2854 assetManager: MoonbeamAssetManagerGroup;2855 assets: AssetsGroup<MoonbeamHelper>;2856 xTokens: XTokensGroup<MoonbeamHelper>;2857 democracy: MoonbeamDemocracyGroup;2858 collective: {2859 council: MoonbeamCollectiveGroup,2860 techCommittee: MoonbeamCollectiveGroup,2861 };28622863 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2864 super(logger, options.helperBase ?? MoonbeamHelper);28652866 this.balance = new EthereumBalanceGroup(this);2867 this.assetManager = new MoonbeamAssetManagerGroup(this);2868 this.assets = new AssetsGroup(this);2869 this.xTokens = new XTokensGroup(this);2870 this.democracy = new MoonbeamDemocracyGroup(this);2871 this.collective = {2872 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2873 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2874 };2875 }2876}28772878export class AcalaHelper extends XcmChainHelper {2879 balance: SubstrateBalanceGroup<AcalaHelper>;2880 assetRegistry: AcalaAssetRegistryGroup;2881 xTokens: XTokensGroup<AcalaHelper>;2882 tokens: TokensGroup<AcalaHelper>;28832884 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2885 super(logger, options.helperBase ?? AcalaHelper);28862887 this.balance = new SubstrateBalanceGroup(this);2888 this.assetRegistry = new AcalaAssetRegistryGroup(this);2889 this.xTokens = new XTokensGroup(this);2890 this.tokens = new TokensGroup(this);2891 }28922893 getSudo<T extends AcalaHelper>() {2894 2895 const SudoHelperType = SudoHelper(this.helperBase);2896 return this.clone(SudoHelperType) as T;2897 }2898}289929002901function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2902 return class extends Base {2903 scheduleFn: 'schedule' | 'scheduleAfter';2904 blocksNum: number;2905 options: ISchedulerOptions;29062907 constructor(...args: any[]) {2908 const logger = args[0] as ILogger;2909 const options = args[1] as {2910 scheduleFn: 'schedule' | 'scheduleAfter',2911 blocksNum: number,2912 options: ISchedulerOptions2913 };29142915 super(logger);29162917 this.scheduleFn = options.scheduleFn;2918 this.blocksNum = options.blocksNum;2919 this.options = options.options;2920 }29212922 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {2923 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);2924 2925 const mandatorySchedArgs = [2926 this.blocksNum,2927 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,2928 this.options.priority ?? null,2929 scheduledTx,2930 ];2931 2932 let schedArgs;2933 let scheduleFn;29342935 if (this.options.scheduledId) {2936 schedArgs = [this.options.scheduledId!, ...mandatorySchedArgs];29372938 if (this.scheduleFn == 'schedule') {2939 scheduleFn = 'scheduleNamed';2940 } else if (this.scheduleFn == 'scheduleAfter') {2941 scheduleFn = 'scheduleNamedAfter';2942 }2943 } else {2944 schedArgs = mandatorySchedArgs;2945 scheduleFn = this.scheduleFn;2946 }29472948 const extrinsic = 'api.tx.scheduler.' + scheduleFn;29492950 return super.executeExtrinsic(2951 sender,2952 extrinsic,2953 schedArgs,2954 expectSuccess,2955 );2956 }2957 };2958}295929602961function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {2962 return class extends Base {2963 constructor(...args: any[]) {2964 super(...args);2965 }29662967 executeExtrinsic (2968 sender: IKeyringPair,2969 extrinsic: string,2970 params: any[],2971 expectSuccess?: boolean,2972 ): Promise<ITransactionResult> {2973 const call = this.constructApiCall(extrinsic, params);2974 return super.executeExtrinsic(2975 sender,2976 'api.tx.sudo.sudo',2977 [call],2978 expectSuccess,2979 );2980 }2981 };2982}29832984export class UniqueBaseCollection {2985 helper: UniqueHelper;2986 collectionId: number;29872988 constructor(collectionId: number, uniqueHelper: UniqueHelper) {2989 this.collectionId = collectionId;2990 this.helper = uniqueHelper;2991 }29922993 async getData() {2994 return await this.helper.collection.getData(this.collectionId);2995 }29962997 async getLastTokenId() {2998 return await this.helper.collection.getLastTokenId(this.collectionId);2999 }30003001 async doesTokenExist(tokenId: number) {3002 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);3003 }30043005 async getAdmins() {3006 return await this.helper.collection.getAdmins(this.collectionId);3007 }30083009 async getAllowList() {3010 return await this.helper.collection.getAllowList(this.collectionId);3011 }30123013 async getEffectiveLimits() {3014 return await this.helper.collection.getEffectiveLimits(this.collectionId);3015 }30163017 async getProperties(propertyKeys?: string[] | null) {3018 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);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 }30883089 scheduleAt<T extends UniqueHelper>(3090 executionBlockNumber: number,3091 options: ISchedulerOptions = {},3092 ) {3093 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3094 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3095 }30963097 scheduleAfter<T extends UniqueHelper>(3098 blocksBeforeExecution: number,3099 options: ISchedulerOptions = {},3100 ) {3101 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3102 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3103 }31043105 getSudo<T extends UniqueHelper>() {3106 return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());3107 }3108}310931103111export class UniqueNFTCollection extends UniqueBaseCollection {3112 getTokenObject(tokenId: number) {3113 return new UniqueNFToken(tokenId, this);3114 }31153116 async getTokensByAddress(addressObj: ICrossAccountId) {3117 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3118 }31193120 async getToken(tokenId: number, blockHashAt?: string) {3121 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3122 }31233124 async getTokenOwner(tokenId: number, blockHashAt?: string) {3125 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3126 }31273128 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3129 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3130 }31313132 async getTokenChildren(tokenId: number, blockHashAt?: string) {3133 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3134 }31353136 async getPropertyPermissions(propertyKeys: string[] | null = null) {3137 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3138 }31393140 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3141 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3142 }31433144 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3145 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3146 }31473148 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3149 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3150 }31513152 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3153 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3154 }31553156 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3157 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3158 }31593160 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3161 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3162 }31633164 async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3165 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3166 }31673168 async burnToken(signer: TSigner, tokenId: number) {3169 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3170 }31713172 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3173 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3174 }31753176 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3177 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3178 }31793180 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3181 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3182 }31833184 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3185 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3186 }31873188 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3189 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3190 }31913192 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3193 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3194 }31953196 scheduleAt<T extends UniqueHelper>(3197 executionBlockNumber: number,3198 options: ISchedulerOptions = {},3199 ) {3200 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3201 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3202 }32033204 scheduleAfter<T extends UniqueHelper>(3205 blocksBeforeExecution: number,3206 options: ISchedulerOptions = {},3207 ) {3208 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3209 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3210 }32113212 getSudo<T extends UniqueHelper>() {3213 return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3214 }3215}321632173218export class UniqueRFTCollection extends UniqueBaseCollection {3219 getTokenObject(tokenId: number) {3220 return new UniqueRFToken(tokenId, this);3221 }32223223 async getToken(tokenId: number, blockHashAt?: string) {3224 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3225 }32263227 async getTokensByAddress(addressObj: ICrossAccountId) {3228 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3229 }32303231 async getTop10TokenOwners(tokenId: number) {3232 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3233 }32343235 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3236 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3237 }32383239 async getTokenTotalPieces(tokenId: number) {3240 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3241 }32423243 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3244 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3245 }32463247 async getPropertyPermissions(propertyKeys: string[] | null = null) {3248 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3249 }32503251 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3252 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3253 }32543255 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3256 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3257 }32583259 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3260 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3261 }32623263 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3264 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3265 }32663267 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3268 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3269 }32703271 async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3272 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3273 }32743275 async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3276 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3277 }32783279 async burnToken(signer: TSigner, tokenId: number, amount=1n) {3280 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3281 }32823283 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n) {3284 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3285 }32863287 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3288 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3289 }32903291 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3292 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3293 }32943295 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3296 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3297 }32983299 scheduleAt<T extends UniqueHelper>(3300 executionBlockNumber: number,3301 options: ISchedulerOptions = {},3302 ) {3303 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3304 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3305 }33063307 scheduleAfter<T extends UniqueHelper>(3308 blocksBeforeExecution: number,3309 options: ISchedulerOptions = {},3310 ) {3311 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3312 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3313 }33143315 getSudo<T extends UniqueHelper>() {3316 return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3317 }3318}331933203321export class UniqueFTCollection extends UniqueBaseCollection {3322 async getBalance(addressObj: ICrossAccountId) {3323 return await this.helper.ft.getBalance(this.collectionId, addressObj);3324 }33253326 async getTotalPieces() {3327 return await this.helper.ft.getTotalPieces(this.collectionId);3328 }33293330 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3331 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3332 }33333334 async getTop10Owners() {3335 return await this.helper.ft.getTop10Owners(this.collectionId);3336 }33373338 async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3339 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3340 }33413342 async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3343 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3344 }33453346 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3347 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3348 }33493350 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3351 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3352 }33533354 async burnTokens(signer: TSigner, amount=1n) {3355 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3356 }33573358 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3359 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3360 }33613362 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3363 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3364 }33653366 scheduleAt<T extends UniqueHelper>(3367 executionBlockNumber: number,3368 options: ISchedulerOptions = {},3369 ) {3370 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(executionBlockNumber, options);3371 return new UniqueFTCollection(this.collectionId, scheduledHelper);3372 }33733374 scheduleAfter<T extends UniqueHelper>(3375 blocksBeforeExecution: number,3376 options: ISchedulerOptions = {},3377 ) {3378 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(blocksBeforeExecution, options);3379 return new UniqueFTCollection(this.collectionId, scheduledHelper);3380 }33813382 getSudo<T extends UniqueHelper>() {3383 return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3384 }3385}338633873388export class UniqueBaseToken {3389 collection: UniqueNFTCollection | UniqueRFTCollection;3390 collectionId: number;3391 tokenId: number;33923393 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3394 this.collection = collection;3395 this.collectionId = collection.collectionId;3396 this.tokenId = tokenId;3397 }33983399 async getNextSponsored(addressObj: ICrossAccountId) {3400 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3401 }34023403 async getProperties(propertyKeys?: string[] | null) {3404 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3405 }34063407 async setProperties(signer: TSigner, properties: IProperty[]) {3408 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3409 }34103411 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3412 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3413 }34143415 async doesExist() {3416 return await this.collection.doesTokenExist(this.tokenId);3417 }34183419 nestingAccount() {3420 return this.collection.helper.util.getTokenAccount(this);3421 }34223423 scheduleAt<T extends UniqueHelper>(3424 executionBlockNumber: number,3425 options: ISchedulerOptions = {},3426 ) {3427 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3428 return new UniqueBaseToken(this.tokenId, scheduledCollection);3429 }34303431 scheduleAfter<T extends UniqueHelper>(3432 blocksBeforeExecution: number,3433 options: ISchedulerOptions = {},3434 ) {3435 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3436 return new UniqueBaseToken(this.tokenId, scheduledCollection);3437 }34383439 getSudo<T extends UniqueHelper>() {3440 return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3441 }3442}344334443445export class UniqueNFToken extends UniqueBaseToken {3446 collection: UniqueNFTCollection;34473448 constructor(tokenId: number, collection: UniqueNFTCollection) {3449 super(tokenId, collection);3450 this.collection = collection;3451 }34523453 async getData(blockHashAt?: string) {3454 return await this.collection.getToken(this.tokenId, blockHashAt);3455 }34563457 async getOwner(blockHashAt?: string) {3458 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3459 }34603461 async getTopmostOwner(blockHashAt?: string) {3462 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3463 }34643465 async getChildren(blockHashAt?: string) {3466 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3467 }34683469 async nest(signer: TSigner, toTokenObj: IToken) {3470 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3471 }34723473 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3474 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3475 }34763477 async transfer(signer: TSigner, addressObj: ICrossAccountId) {3478 return await this.collection.transferToken(signer, this.tokenId, addressObj);3479 }34803481 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3482 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3483 }34843485 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3486 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3487 }34883489 async isApproved(toAddressObj: ICrossAccountId) {3490 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3491 }34923493 async burn(signer: TSigner) {3494 return await this.collection.burnToken(signer, this.tokenId);3495 }34963497 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3498 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3499 }35003501 scheduleAt<T extends UniqueHelper>(3502 executionBlockNumber: number,3503 options: ISchedulerOptions = {},3504 ) {3505 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3506 return new UniqueNFToken(this.tokenId, scheduledCollection);3507 }35083509 scheduleAfter<T extends UniqueHelper>(3510 blocksBeforeExecution: number,3511 options: ISchedulerOptions = {},3512 ) {3513 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3514 return new UniqueNFToken(this.tokenId, scheduledCollection);3515 }35163517 getSudo<T extends UniqueHelper>() {3518 return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3519 }3520}35213522export class UniqueRFToken extends UniqueBaseToken {3523 collection: UniqueRFTCollection;35243525 constructor(tokenId: number, collection: UniqueRFTCollection) {3526 super(tokenId, collection);3527 this.collection = collection;3528 }35293530 async getData(blockHashAt?: string) {3531 return await this.collection.getToken(this.tokenId, blockHashAt);3532 }35333534 async getTop10Owners() {3535 return await this.collection.getTop10TokenOwners(this.tokenId);3536 }35373538 async getBalance(addressObj: ICrossAccountId) {3539 return await this.collection.getTokenBalance(this.tokenId, addressObj);3540 }35413542 async getTotalPieces() {3543 return await this.collection.getTokenTotalPieces(this.tokenId);3544 }35453546 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3547 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3548 }35493550 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3551 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3552 }35533554 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3555 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3556 }35573558 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3559 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3560 }35613562 async repartition(signer: TSigner, amount: bigint) {3563 return await this.collection.repartitionToken(signer, this.tokenId, amount);3564 }35653566 async burn(signer: TSigner, amount=1n) {3567 return await this.collection.burnToken(signer, this.tokenId, amount);3568 }35693570 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3571 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3572 }35733574 scheduleAt<T extends UniqueHelper>(3575 executionBlockNumber: number,3576 options: ISchedulerOptions = {},3577 ) {3578 const scheduledCollection = this.collection.scheduleAt<T>(executionBlockNumber, options);3579 return new UniqueRFToken(this.tokenId, scheduledCollection);3580 }35813582 scheduleAfter<T extends UniqueHelper>(3583 blocksBeforeExecution: number,3584 options: ISchedulerOptions = {},3585 ) {3586 const scheduledCollection = this.collection.scheduleAfter<T>(blocksBeforeExecution, options);3587 return new UniqueRFToken(this.tokenId, scheduledCollection);3588 }35893590 getSudo<T extends UniqueHelper>() {3591 return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3592 }3593}