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} from './types';44import {RuntimeDispatchInfo} from '@polkadot/types/interfaces';4546export class CrossAccountId implements ICrossAccountId {47 Substrate?: TSubstrateAccount;48 Ethereum?: TEthereumAccount;4950 constructor(account: ICrossAccountId) {51 if (account.Substrate) this.Substrate = account.Substrate;52 if (account.Ethereum) this.Ethereum = account.Ethereum;53 }5455 static fromKeyring(account: IKeyringPair, domain: 'Substrate' | 'Ethereum' = 'Substrate') {56 switch (domain) {57 case 'Substrate': return new CrossAccountId({Substrate: account.address});58 case 'Ethereum': return new CrossAccountId({Substrate: account.address}).toEthereum();59 }60 }6162 static fromLowerCaseKeys(address: ICrossAccountIdLower): CrossAccountId {63 return new CrossAccountId({Substrate: address.substrate, Ethereum: address.ethereum});64 }6566 static normalizeSubstrateAddress(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {67 return encodeAddress(decodeAddress(address), ss58Format);68 }6970 static withNormalizedSubstrate(address: TSubstrateAccount, ss58Format = 42): CrossAccountId {71 return new CrossAccountId({Substrate: CrossAccountId.normalizeSubstrateAddress(address, ss58Format)});72 }7374 withNormalizedSubstrate(ss58Format = 42): CrossAccountId {75 if (this.Substrate) return CrossAccountId.withNormalizedSubstrate(this.Substrate, ss58Format);76 return this;77 }7879 static translateSubToEth(address: TSubstrateAccount): TEthereumAccount {80 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(address), i => i.toString(16).padStart(2, '0')).join(''));81 }8283 toEthereum(): CrossAccountId {84 if (this.Substrate) return new CrossAccountId({Ethereum: CrossAccountId.translateSubToEth(this.Substrate)});85 return this;86 }8788 static translateEthToSub(address: TEthereumAccount, ss58Format?: number): TSubstrateAccount {89 return evmToAddress(address, ss58Format);90 }9192 toSubstrate(ss58Format?: number): CrossAccountId {93 if (this.Ethereum) return new CrossAccountId({Substrate: CrossAccountId.translateEthToSub(this.Ethereum, ss58Format)});94 return this;95 }9697 toLowerCase(): CrossAccountId {98 if (this.Substrate) this.Substrate = this.Substrate.toLowerCase();99 if (this.Ethereum) this.Ethereum = this.Ethereum.toLowerCase();100 return this;101 }102}103104const nesting = {105 toChecksumAddress(address: string): string {106 if (typeof address === 'undefined') return '';107108 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);109110 address = address.toLowerCase().replace(/^0x/i,'');111 const addressHash = keccakAsHex(address).replace(/^0x/i,'');112 const checksumAddress = ['0x'];113114 for (let i = 0; i < address.length; i++) {115 116 if (parseInt(addressHash[i], 16) > 7) {117 checksumAddress.push(address[i].toUpperCase());118 } else {119 checksumAddress.push(address[i]);120 }121 }122 return checksumAddress.join('');123 },124 tokenIdToAddress(collectionId: number, tokenId: number) {125 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);126 },127};128129class UniqueUtil {130 static transactionStatus = {131 NOT_READY: 'NotReady',132 FAIL: 'Fail',133 SUCCESS: 'Success',134 };135136 static chainLogType = {137 EXTRINSIC: 'extrinsic',138 RPC: 'rpc',139 };140141 static getTokenAccount(token: IToken): CrossAccountId {142 return new CrossAccountId({Ethereum: this.getTokenAddress(token)});143 }144145 static getTokenAddress(token: IToken): string {146 return nesting.tokenIdToAddress(token.collectionId, token.tokenId);147 }148149 static getDefaultLogger(): ILogger {150 return {151 log(msg: any, level = 'INFO') {152 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));153 },154 level: {155 ERROR: 'ERROR',156 WARNING: 'WARNING',157 INFO: 'INFO',158 },159 };160 }161162 static vec2str(arr: string[] | number[]) {163 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');164 }165166 static str2vec(string: string) {167 if (typeof string !== 'string') return string;168 return Array.from(string).map(x => x.charCodeAt(0));169 }170171 static fromSeed(seed: string, ss58Format = 42) {172 const keyring = new Keyring({type: 'sr25519', ss58Format});173 return keyring.addFromUri(seed);174 }175176 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult): number {177 if (creationResult.status !== this.transactionStatus.SUCCESS) {178 throw Error('Unable to create collection!');179 }180181 let collectionId = null;182 creationResult.result.events.forEach(({event: {data, method, section}}) => {183 if ((section === 'common') && (method === 'CollectionCreated')) {184 collectionId = parseInt(data[0].toString(), 10);185 }186 });187188 if (collectionId === null) {189 throw Error('No CollectionCreated event was found!');190 }191192 return collectionId;193 }194195 static extractTokensFromCreationResult(creationResult: ITransactionResult): {196 success: boolean,197 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],198 } {199 if (creationResult.status !== this.transactionStatus.SUCCESS) {200 throw Error('Unable to create tokens!');201 }202 let success = false;203 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];204 creationResult.result.events.forEach(({event: {data, method, section}}) => {205 if (method === 'ExtrinsicSuccess') {206 success = true;207 } else if ((section === 'common') && (method === 'ItemCreated')) {208 tokens.push({209 collectionId: parseInt(data[0].toString(), 10),210 tokenId: parseInt(data[1].toString(), 10),211 owner: data[2].toHuman(),212 amount: data[3].toBigInt(),213 });214 }215 });216 return {success, tokens};217 }218219 static extractTokensFromBurnResult(burnResult: ITransactionResult): {220 success: boolean,221 tokens: {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[],222 } {223 if (burnResult.status !== this.transactionStatus.SUCCESS) {224 throw Error('Unable to burn tokens!');225 }226 let success = false;227 const tokens = [] as {collectionId: number, tokenId: number, owner: CrossAccountId, amount: bigint}[];228 burnResult.result.events.forEach(({event: {data, method, section}}) => {229 if (method === 'ExtrinsicSuccess') {230 success = true;231 } else if ((section === 'common') && (method === 'ItemDestroyed')) {232 tokens.push({233 collectionId: parseInt(data[0].toString(), 10),234 tokenId: parseInt(data[1].toString(), 10),235 owner: data[2].toHuman(),236 amount: data[3].toBigInt(),237 });238 }239 });240 return {success, tokens};241 }242243 static findCollectionInEvents(events: {event: IEvent}[], collectionId: number, expectedSection: string, expectedMethod: string): boolean {244 let eventId = null;245 events.forEach(({event: {data, method, section}}) => {246 if ((section === expectedSection) && (method === expectedMethod)) {247 eventId = parseInt(data[0].toString(), 10);248 }249 });250251 if (eventId === null) {252 throw Error(`No ${expectedMethod} event was found!`);253 }254 return eventId === collectionId;255 }256257 static isTokenTransferSuccess(events: {event: IEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {258 const normalizeAddress = (address: string | ICrossAccountId) => {259 if(typeof address === 'string') return address;260 const obj = {} as any;261 Object.keys(address).forEach(k => {262 obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];263 });264 if(obj.substrate) return CrossAccountId.withNormalizedSubstrate(obj.substrate);265 if(obj.ethereum) return CrossAccountId.fromLowerCaseKeys(obj).toLowerCase();266 return address;267 };268 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;269 events.forEach(({event: {data, method, section}}) => {270 if ((section === 'common') && (method === 'Transfer')) {271 const hData = (data as any).toJSON();272 transfer = {273 collectionId: hData[0],274 tokenId: hData[1],275 from: normalizeAddress(hData[2]),276 to: normalizeAddress(hData[3]),277 amount: BigInt(hData[4]),278 };279 }280 });281 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;282 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);283 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);284 isSuccess = isSuccess && amount === transfer.amount;285 return isSuccess;286 }287288 static bigIntToDecimals(number: bigint, decimals = 18) {289 const numberStr = number.toString();290 const dotPos = numberStr.length - decimals;291292 if (dotPos <= 0) {293 return '0.' + '0'.repeat(Math.abs(dotPos)) + numberStr;294 } else {295 const intPart = numberStr.substring(0, dotPos);296 const fractPart = numberStr.substring(dotPos);297 return intPart + '.' + fractPart;298 }299 }300}301302class UniqueEventHelper {303 private static extractIndex(index: any): [number, number] | string {304 if(index.toRawType() === '[u8;2]') return [index[0], index[1]];305 return index.toJSON();306 }307308 private static extractSub(data: any, subTypes: any): {[key: string]: any} {309 let obj: any = {};310 let index = 0;311312 if (data.entries) {313 for(const [key, value] of data.entries()) {314 obj[key] = this.extractData(value, subTypes[index]);315 index++;316 }317 } else obj = data.toJSON();318319 return obj;320 }321322 private static extractData(data: any, type: any): any {323 if(!type) return data.toHuman();324 if (['u16', 'u32'].indexOf(type.type) > -1) return data.toNumber();325 if (['u64', 'u128', 'u256'].indexOf(type.type) > -1) return data.toBigInt();326 if(type.hasOwnProperty('sub')) return this.extractSub(data, type.sub);327 return data.toHuman();328 }329330 public static extractEvents(events: {event: any, phase: any}[]): IEvent[] {331 const parsedEvents: IEvent[] = [];332333 events.forEach((record) => {334 const {event, phase} = record;335 const types = event.typeDef;336337 const eventData: IEvent = {338 section: event.section.toString(),339 method: event.method.toString(),340 index: this.extractIndex(event.index),341 data: [],342 phase: phase.toJSON(),343 };344345 event.data.forEach((val: any, index: number) => {346 eventData.data.push(this.extractData(val, types[index]));347 });348349 parsedEvents.push(eventData);350 });351352 return parsedEvents;353 }354}355356export class ChainHelperBase {357 helperBase: any;358359 transactionStatus = UniqueUtil.transactionStatus;360 chainLogType = UniqueUtil.chainLogType;361 util: typeof UniqueUtil;362 eventHelper: typeof UniqueEventHelper;363 logger: ILogger;364 api: ApiPromise | null;365 forcedNetwork: TNetworks | null;366 network: TNetworks | null;367 chainLog: IUniqueHelperLog[];368 children: ChainHelperBase[];369 address: AddressGroup;370 chain: ChainGroup;371372 constructor(logger?: ILogger, helperBase?: any) {373 this.helperBase = helperBase;374375 this.util = UniqueUtil;376 this.eventHelper = UniqueEventHelper;377 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();378 this.logger = logger;379 this.api = null;380 this.forcedNetwork = null;381 this.network = null;382 this.chainLog = [];383 this.children = [];384 this.address = new AddressGroup(this);385 this.chain = new ChainGroup(this);386 }387388 clone(helperCls: ChainHelperBaseConstructor, options: {[key: string]: any} = {}) {389 Object.setPrototypeOf(helperCls.prototype, this);390 const newHelper = new helperCls(this.logger, options);391392 newHelper.api = this.api;393 newHelper.network = this.network;394 newHelper.forceNetwork = this.forceNetwork;395396 this.children.push(newHelper);397398 return newHelper;399 }400401 getApi(): ApiPromise {402 if(this.api === null) throw Error('API not initialized');403 return this.api;404 }405406 clearChainLog(): void {407 this.chainLog = [];408 }409410 forceNetwork(value: TNetworks): void {411 this.forcedNetwork = value;412 }413414 async connect(wsEndpoint: string, listeners?: IApiListeners) {415 if (this.api !== null) throw Error('Already connected');416 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);417 this.api = api;418 this.network = network;419 }420421 async disconnect() {422 for (const child of this.children) {423 child.clearApi();424 }425426 if (this.api === null) return;427 await this.api.disconnect();428 this.clearApi();429 }430431 clearApi() {432 this.api = null;433 this.network = null;434 }435436 static async detectNetwork(api: ApiPromise): Promise<TNetworks> {437 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;438 const xcmChains = ['rococo', 'westend', 'westmint', 'acala', 'karura', 'moonbeam', 'moonriver'];439440 if(xcmChains.indexOf(spec.specName) > -1) return spec.specName;441442 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;443 return 'opal';444 }445446 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TNetworks> {447 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});448 await api.isReady;449450 const network = await this.detectNetwork(api);451452 await api.disconnect();453454 return network;455 }456457 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TNetworks | null): Promise<{458 api: ApiPromise;459 network: TNetworks;460 }> {461 if(typeof network === 'undefined' || network === null) network = 'opal';462 const supportedRPC = {463 opal: {464 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,465 },466 quartz: {467 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,468 },469 unique: {470 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,471 },472 rococo: {},473 westend: {},474 moonbeam: {},475 moonriver: {},476 acala: {},477 karura: {},478 westmint: {},479 };480 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);481 const rpc = supportedRPC[network];482483 484 485486 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});487488 await api.isReadyOrError;489490 if (typeof listeners === 'undefined') listeners = {};491 for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {492 if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;493 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);494 }495496 return {api, network};497 }498499 getTransactionStatus(data: {events: {event: IEvent}[], status: any}) {500 const {events, status} = data;501 if (status.isReady) {502 return this.transactionStatus.NOT_READY;503 }504 if (status.isBroadcast) {505 return this.transactionStatus.NOT_READY;506 }507 if (status.isInBlock || status.isFinalized) {508 const errors = events.filter(e => e.event.method === 'ExtrinsicFailed');509 if (errors.length > 0) {510 return this.transactionStatus.FAIL;511 }512 if (events.filter(e => e.event.method === 'ExtrinsicSuccess').length > 0) {513 return this.transactionStatus.SUCCESS;514 }515 }516517 return this.transactionStatus.FAIL;518 }519520 signTransaction(sender: TSigner, transaction: any, options: Partial<SignerOptions> | null = null, label = 'transaction') {521 const sign = (callback: any) => {522 if(options !== null) return transaction.signAndSend(sender, options, callback);523 return transaction.signAndSend(sender, callback);524 };525 526 return new Promise(async (resolve, reject) => {527 try {528 const unsub = await sign((result: any) => {529 const status = this.getTransactionStatus(result);530531 if (status === this.transactionStatus.SUCCESS) {532 this.logger.log(`${label} successful`);533 unsub();534 resolve({result, status});535 } else if (status === this.transactionStatus.FAIL) {536 let moduleError = null;537538 if (result.hasOwnProperty('dispatchError')) {539 const dispatchError = result['dispatchError'];540541 if (dispatchError) {542 if (dispatchError.isModule) {543 const modErr = dispatchError.asModule;544 const errorMeta = dispatchError.registry.findMetaError(modErr);545546 moduleError = `${errorMeta.section}.${errorMeta.name}`;547 } else {548 moduleError = dispatchError.toHuman();549 }550 } else {551 this.logger.log(result, this.logger.level.ERROR);552 }553 }554555 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);556 unsub();557 reject({status, moduleError, result});558 }559 });560 } catch (e) {561 this.logger.log(e, this.logger.level.ERROR);562 reject(e);563 }564 });565 }566567 async getPaymentInfo(signer: TSigner, tx: any, len: number | null) {568 const api = this.getApi();569 const signingInfo = await api.derive.tx.signingInfo(signer.address);570571 572 573 tx.sign(signer, {574 blockHash: api.genesisHash,575 genesisHash: api.genesisHash,576 runtimeVersion: api.runtimeVersion,577 nonce: signingInfo.nonce,578 });579580 if (len === null) {581 return (await this.callRpc('api.rpc.payment.queryInfo', [tx.toHex()])) as RuntimeDispatchInfo;582 } else {583 return (await api.call.transactionPaymentApi.queryInfo(tx, len)) as RuntimeDispatchInfo;584 }585 }586587 constructApiCall(apiCall: string, params: any[]) {588 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);589 let call = this.getApi() as any;590 for(const part of apiCall.slice(4).split('.')) {591 call = call[part];592 }593 return call(...params);594 }595596 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=true, options: Partial<SignerOptions>|null = null) {597 if(this.api === null) throw Error('API not initialized');598 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);599600 const startTime = (new Date()).getTime();601 let result: ITransactionResult;602 let events: IEvent[] = [];603 try {604 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), options, extrinsic) as ITransactionResult;605 events = this.eventHelper.extractEvents(result.result.events);606 }607 catch(e) {608 if(!(e as object).hasOwnProperty('status')) throw e;609 result = e as ITransactionResult;610 }611612 const endTime = (new Date()).getTime();613614 const log = {615 executedAt: endTime,616 executionTime: endTime - startTime,617 type: this.chainLogType.EXTRINSIC,618 status: result.status,619 call: extrinsic,620 signer: this.getSignerAddress(sender),621 params,622 } as IUniqueHelperLog;623624 if(result.status !== this.transactionStatus.SUCCESS) {625 if (result.moduleError) log.moduleError = result.moduleError;626 else if (result.result.dispatchError) log.dispatchError = result.result.dispatchError;627 }628 if(events.length > 0) log.events = events;629630 this.chainLog.push(log);631632 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) {633 if (result.moduleError) throw Error(`${result.moduleError}`);634 else if (result.result.dispatchError) throw Error(JSON.stringify(result.result.dispatchError));635 }636 return result;637 }638639 async callRpc(rpc: string, params?: any[]) {640 if(typeof params === 'undefined') params = [];641 if(this.api === null) throw Error('API not initialized');642 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);643644 const startTime = (new Date()).getTime();645 let result;646 let error = null;647 const log = {648 type: this.chainLogType.RPC,649 call: rpc,650 params,651 } as IUniqueHelperLog;652653 try {654 result = await this.constructApiCall(rpc, params);655 }656 catch(e) {657 error = e;658 }659660 const endTime = (new Date()).getTime();661662 log.executedAt = endTime;663 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';664 log.executionTime = endTime - startTime;665666 this.chainLog.push(log);667668 if(error !== null) throw error;669670 return result;671 }672673 getSignerAddress(signer: IKeyringPair | string): string {674 if(typeof signer === 'string') return signer;675 return signer.address;676 }677678 fetchAllPalletNames(): string[] {679 if(this.api === null) throw Error('API not initialized');680 return this.api.runtimeMetadata.asLatest.pallets.map(m => m.name.toString().toLowerCase());681 }682683 fetchMissingPalletNames(requiredPallets: string[]): string[] {684 const palletNames = this.fetchAllPalletNames();685 return requiredPallets.filter(p => !palletNames.includes(p));686 }687}688689690class HelperGroup<T extends ChainHelperBase> {691 helper: T;692693 constructor(uniqueHelper: T) {694 this.helper = uniqueHelper;695 }696}697698699class CollectionGroup extends HelperGroup<UniqueHelper> {700 701702703704705706707708709 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {710 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();711 }712713 714715716717718 async getTotalCount(): Promise<number> {719 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();720 }721722 723724725726727728729730731 async getData(collectionId: number): Promise<{732 id: number;733 name: string;734 description: string;735 tokensCount: number;736 admins: CrossAccountId[];737 normalizedOwner: TSubstrateAccount;738 raw: any739 } | null> {740 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);741 const humanCollection = collection.toHuman(), collectionData = {742 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],743 raw: humanCollection,744 } as any, jsonCollection = collection.toJSON();745 if (humanCollection === null) return null;746 collectionData.raw.limits = jsonCollection.limits;747 collectionData.raw.permissions = jsonCollection.permissions;748 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);749 for (const key of ['name', 'description']) {750 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);751 }752753 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode))754 ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId)755 : 0;756 collectionData.admins = await this.getAdmins(collectionId);757758 return collectionData;759 }760761 762763764765766767768769 async getAdmins(collectionId: number, normalize = false): Promise<CrossAccountId[]> {770 const admins = (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman();771772 return normalize773 ? admins.map((address: CrossAccountId) => address.withNormalizedSubstrate())774 : admins;775 }776777 778779780781782783784 async getAllowList(collectionId: number, normalize = false): Promise<CrossAccountId[]> {785 const allowListed = (await this.helper.callRpc('api.rpc.unique.allowlist', [collectionId])).toHuman();786 return normalize787 ? allowListed.map((address: CrossAccountId) => address.withNormalizedSubstrate())788 : allowListed;789 }790791 792793794795796797798 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {799 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();800 }801802 803804805806807808809810 async burn(signer: TSigner, collectionId: number): Promise<boolean> {811 const result = await this.helper.executeExtrinsic(812 signer,813 'api.tx.unique.destroyCollection', [collectionId],814 true,815 );816817 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed');818 }819820 821822823824825826827828829 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount): Promise<boolean> {830 const result = await this.helper.executeExtrinsic(831 signer,832 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],833 true,834 );835836 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet');837 }838839 840841842843844845846847 async confirmSponsorship(signer: TSigner, collectionId: number): Promise<boolean> {848 const result = await this.helper.executeExtrinsic(849 signer,850 'api.tx.unique.confirmSponsorship', [collectionId],851 true,852 );853854 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed');855 }856857 858859860861862863864865 async removeSponsor(signer: TSigner, collectionId: number): Promise<boolean> {866 const result = await this.helper.executeExtrinsic(867 signer,868 'api.tx.unique.removeCollectionSponsor', [collectionId],869 true,870 );871872 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorRemoved');873 }874875 876877878879880881882883884885886887888889890891892 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits): Promise<boolean> {893 const result = await this.helper.executeExtrinsic(894 signer,895 'api.tx.unique.setCollectionLimits', [collectionId, limits],896 true,897 );898899 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet');900 }901902 903904905906907908909910911 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount): Promise<boolean> {912 const result = await this.helper.executeExtrinsic(913 signer,914 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],915 true,916 );917918 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged');919 }920921 922923924925926927928929930 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {931 const result = await this.helper.executeExtrinsic(932 signer,933 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],934 true,935 );936937 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded');938 }939940 941942943944945946947948949 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId): Promise<boolean> {950 const result = await this.helper.executeExtrinsic(951 signer,952 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],953 true,954 );955956 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved');957 }958959 960961962963964965966967 async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {968 return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();969 }970971 972973974975976977978 async addToAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {979 const result = await this.helper.executeExtrinsic(980 signer,981 'api.tx.unique.addToAllowList', [collectionId, addressObj],982 true,983 );984985 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressAdded');986 }987988 989990991992993994995996 async removeFromAllowList(signer: TSigner, collectionId: number, addressObj: ICrossAccountId): Promise<boolean> {997 const result = await this.helper.executeExtrinsic(998 signer,999 'api.tx.unique.removeFromAllowList', [collectionId, addressObj],1000 true,1001 );10021003 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'AllowListAddressRemoved');1004 }10051006 100710081009101010111012101310141015 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions): Promise<boolean> {1016 const result = await this.helper.executeExtrinsic(1017 signer,1018 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],1019 true,1020 );10211022 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet');1023 }10241025 102610271028102910301031103210331034 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions): Promise<boolean> {1035 return await this.setPermissions(signer, collectionId, {nesting: permissions});1036 }10371038 10391040104110421043104410451046 async disableNesting(signer: TSigner, collectionId: number): Promise<boolean> {1047 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}});1048 }10491050 105110521053105410551056105710581059 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[]): Promise<boolean> {1060 const result = await this.helper.executeExtrinsic(1061 signer,1062 'api.tx.unique.setCollectionProperties', [collectionId, properties],1063 true,1064 );10651066 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet');1067 }10681069 10701071107210731074107510761077 async getProperties(collectionId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1078 return (await this.helper.callRpc('api.rpc.unique.collectionProperties', [collectionId, propertyKeys])).toHuman();1079 }10801081 async getCollectionOptions(collectionId: number) {1082 return (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1083 }10841085 108610871088108910901091109210931094 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[]): Promise<boolean> {1095 const result = await this.helper.executeExtrinsic(1096 signer,1097 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],1098 true,1099 );11001101 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted');1102 }11031104 11051106110711081109111011111112111311141115 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1116 const result = await this.helper.executeExtrinsic(1117 signer,1118 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],1119 true, 1120 );11211122 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);1123 }11241125 1126112711281129113011311132113311341135113611371138 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1139 const result = await this.helper.executeExtrinsic(1140 signer,1141 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],1142 true, 1143 );1144 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1145 }11461147 11481149115011511152115311541155115611571158 async burnToken(signer: TSigner, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1159 const burnResult = await this.helper.executeExtrinsic(1160 signer,1161 'api.tx.unique.burnItem', [collectionId, tokenId, amount],1162 true, 1163 );1164 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1165 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');1166 return burnedTokens.success;1167 }11681169 11701171117211731174117511761177117811791180 async burnTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1181 const burnResult = await this.helper.executeExtrinsic(1182 signer,1183 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],1184 true, 1185 );1186 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult);1187 return burnedTokens.success && burnedTokens.tokens.length > 0;1188 }11891190 1191119211931194119511961197119811991200 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1201 const approveResult = await this.helper.executeExtrinsic(1202 signer,1203 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1204 true, 1205 );12061207 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved');1208 }12091210 1211121212131214121512161217121812191220 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1221 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1222 }12231224 1225122612271228122912301231 async getLastTokenId(collectionId: number): Promise<number> {1232 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1233 }12341235 12361237123812391240124112421243 async doesTokenExist(collectionId: number, tokenId: number): Promise<boolean> {1244 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1245 }1246}12471248class NFTnRFT extends CollectionGroup {1249 12501251125212531254125512561257 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1258 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1259 }12601261 1262126312641265126612671268126912701271 async getToken(collectionId: number, tokenId: number, propertyKeys: string[] = [], blockHashAt?: string): Promise<{1272 properties: IProperty[];1273 owner: CrossAccountId;1274 normalizedOwner: CrossAccountId;1275 }| null> {1276 let tokenData;1277 if(typeof blockHashAt === 'undefined') {1278 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1279 }1280 else {1281 if(propertyKeys.length == 0) {1282 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1283 if(!collection) return null;1284 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1285 }1286 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1287 }1288 tokenData = tokenData.toHuman();1289 if (tokenData === null || tokenData.owner === null) return null;1290 const owner = {} as any;1291 for (const key of Object.keys(tokenData.owner)) {1292 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() == 'substrate'1293 ? CrossAccountId.normalizeSubstrateAddress(tokenData.owner[key])1294 : tokenData.owner[key];1295 }1296 tokenData.normalizedOwner = CrossAccountId.fromLowerCaseKeys(owner);1297 return tokenData;1298 }12991300 13011302130313041305130613071308130913101311 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[]): Promise<boolean> {1312 const result = await this.helper.executeExtrinsic(1313 signer,1314 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1315 true,1316 );13171318 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet');1319 }13201321 13221323132413251326132713281329 async getPropertyPermissions(collectionId: number, propertyKeys: string[] | null = null): Promise<ITokenPropertyPermission[]> {1330 return (await this.helper.callRpc('api.rpc.unique.propertyPermissions', [collectionId, ...(propertyKeys === null ? [] : [propertyKeys])])).toHuman();1331 }13321333 1334133513361337133813391340134113421343 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[]): Promise<boolean> {1344 const result = await this.helper.executeExtrinsic(1345 signer,1346 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1347 true,1348 );13491350 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet');1351 }13521353 135413551356135713581359136013611362 async getTokenProperties(collectionId: number, tokenId: number, propertyKeys?: string[] | null): Promise<IProperty[]> {1363 return (await this.helper.callRpc('api.rpc.unique.tokenProperties', [collectionId, tokenId, propertyKeys])).toHuman();1364 }13651366 136713681369137013711372137313741375 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[]): Promise<boolean> {1376 const result = await this.helper.executeExtrinsic(1377 signer,1378 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1379 true,1380 );13811382 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted');1383 }13841385 138613871388138913901391139213931394 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT'): Promise<UniqueBaseCollection> {1395 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1396 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1397 for (const key of ['name', 'description', 'tokenPrefix']) {1398 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);1399 }1400 const creationResult = await this.helper.executeExtrinsic(1401 signer,1402 'api.tx.unique.createCollectionEx', [collectionOptions],1403 true, 1404 );1405 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1406 }14071408 getCollectionObject(_collectionId: number): any {1409 return null;1410 }14111412 getTokenObject(_collectionId: number, _tokenId: number): any {1413 return null;1414 }1415}141614171418class NFTGroup extends NFTnRFT {1419 142014211422142314241425 getCollectionObject(collectionId: number): UniqueNFTCollection {1426 return new UniqueNFTCollection(collectionId, this.helper);1427 }14281429 1430143114321433143414351436 getTokenObject(collectionId: number, tokenId: number): UniqueNFToken {1437 return new UniqueNFToken(tokenId, this.getCollectionObject(collectionId));1438 }14391440 14411442144314441445144614471448 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId> {1449 let owner;1450 if (typeof blockHashAt === 'undefined') {1451 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1452 } else {1453 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1454 }1455 return CrossAccountId.fromLowerCaseKeys(owner.toJSON());1456 }14571458 1459146014611462146314641465 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1466 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1467 }14681469 1470147114721473147414751476147714781479 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1480 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1481 }14821483 148414851486148714881489149014911492149314941495 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1496 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1497 }14981499 15001501150215031504150515061507 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<CrossAccountId | null> {1508 let owner;1509 if (typeof blockHashAt === 'undefined') {1510 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1511 } else {1512 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1513 }15141515 if (owner === null) return null;15161517 return owner.toHuman();1518 }15191520 15211522152315241525152615271528 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1529 let children;1530 if(typeof blockHashAt === 'undefined') {1531 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1532 } else {1533 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1534 }15351536 return children.toJSON().map((x: any) => {1537 return {collectionId: x.collection, tokenId: x.token};1538 });1539 }15401541 15421543154415451546154715481549 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken): Promise<boolean> {1550 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1551 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1552 if(!result) {1553 throw Error('Unable to nest token!');1554 }1555 return result;1556 }15571558 155915601561156215631564156515661567 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId): Promise<boolean> {1568 const rootTokenAddress = this.helper.util.getTokenAccount(rootTokenObj);1569 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1570 if(!result) {1571 throw Error('Unable to unnest token!');1572 }1573 return result;1574 }15751576 157715781579158015811582158315841585158615871588 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueNFTCollection> {1589 return await super.mintCollection(signer, collectionOptions, 'NFT') as UniqueNFTCollection;1590 }15911592 159315941595159615971598 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }): Promise<UniqueNFToken> {1599 const creationResult = await this.helper.executeExtrinsic(1600 signer,1601 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1602 nft: {1603 properties: data.properties,1604 },1605 }],1606 true,1607 );1608 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1609 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1610 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1611 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1612 }16131614 161516161617161816191620162116221623162416251626162716281629 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1630 const creationResult = await this.helper.executeExtrinsic(1631 signer,1632 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1633 true,1634 );1635 const collection = this.getCollectionObject(collectionId);1636 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1637 }16381639 164016411642164316441645164616471648164916501651165216531654165516561657 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[]): Promise<UniqueNFToken[]> {1658 const rawTokens = [];1659 for (const token of tokens) {1660 const raw = {NFT: {properties: token.properties}};1661 rawTokens.push(raw);1662 }1663 const creationResult = await this.helper.executeExtrinsic(1664 signer,1665 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1666 true,1667 );1668 const collection = this.getCollectionObject(collectionId);1669 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1670 }16711672 1673167416751676167716781679168016811682 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId) {1683 return super.approveToken(signer, collectionId, tokenId, toAddressObj, 1n);1684 }1685}168616871688class RFTGroup extends NFTnRFT {1689 169016911692169316941695 getCollectionObject(collectionId: number): UniqueRFTCollection {1696 return new UniqueRFTCollection(collectionId, this.helper);1697 }16981699 1700170117021703170417051706 getTokenObject(collectionId: number, tokenId: number): UniqueRFToken {1707 return new UniqueRFToken(tokenId, this.getCollectionObject(collectionId));1708 }17091710 1711171217131714171517161717 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<CrossAccountId[]> {1718 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(CrossAccountId.fromLowerCaseKeys);1719 }17201721 17221723172417251726172717281729 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1730 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1731 }17321733 1734173517361737173817391740174117421743 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {1744 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1745 }17461747 17481749175017511752175317541755175617571758 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1759 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1760 }17611762 176317641765176617671768176917701771177217731774 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}): Promise<UniqueRFTCollection> {1775 return await super.mintCollection(signer, collectionOptions, 'RFT') as UniqueRFTCollection;1776 }17771778 1779178017811782178317841785 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }): Promise<UniqueRFToken> {1786 const creationResult = await this.helper.executeExtrinsic(1787 signer,1788 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1789 refungible: {1790 pieces: data.pieces,1791 properties: data.properties,1792 },1793 }],1794 true,1795 );1796 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult);1797 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1798 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1799 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1800 }18011802 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1803 throw Error('Not implemented');1804 const creationResult = await this.helper.executeExtrinsic(1805 signer,1806 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1807 true, 1808 );1809 const collection = this.getCollectionObject(collectionId);1810 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1811 }18121813 181418151816181718181819182018211822 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[]): Promise<UniqueRFToken[]> {1823 const rawTokens = [];1824 for (const token of tokens) {1825 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1826 rawTokens.push(raw);1827 }1828 const creationResult = await this.helper.executeExtrinsic(1829 signer,1830 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1831 true,1832 );1833 const collection = this.getCollectionObject(collectionId);1834 return this.helper.util.extractTokensFromCreationResult(creationResult).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1835 }18361837 183818391840184118421843184418451846 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, amount=1n): Promise<boolean> {1847 return await super.burnToken(signer, collectionId, tokenId, amount);1848 }18491850 1851185218531854185518561857185818591860 async burnTokenFrom(signer: IKeyringPair, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {1861 return await super.burnTokenFrom(signer, collectionId, tokenId, fromAddressObj, amount);1862 }18631864 18651866186718681869187018711872187318741875 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {1876 return super.approveToken(signer, collectionId, tokenId, toAddressObj, amount);1877 }18781879 1880188118821883188418851886 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1887 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1888 }18891890 189118921893189418951896189718981899 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint): Promise<boolean> {1900 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1901 const repartitionResult = await this.helper.executeExtrinsic(1902 signer,1903 'api.tx.unique.repartition', [collectionId, tokenId, amount],1904 true,1905 );1906 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated');1907 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed');1908 }1909}191019111912class FTGroup extends CollectionGroup {1913 191419151916191719181919 getCollectionObject(collectionId: number): UniqueFTCollection {1920 return new UniqueFTCollection(collectionId, this.helper);1921 }19221923 1924192519261927192819291930193119321933193419351936 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions = {}, decimalPoints = 0): Promise<UniqueFTCollection> {1937 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1938 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1939 collectionOptions.mode = {fungible: decimalPoints};1940 for (const key of ['name', 'description', 'tokenPrefix']) {1941 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);1942 }1943 const creationResult = await this.helper.executeExtrinsic(1944 signer,1945 'api.tx.unique.createCollectionEx', [collectionOptions],1946 true,1947 );1948 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult));1949 }19501951 195219531954195519561957195819591960 async mintTokens(signer: TSigner, collectionId: number, amount: bigint, owner: ICrossAccountId | string): Promise<boolean> {1961 const creationResult = await this.helper.executeExtrinsic(1962 signer,1963 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {1964 fungible: {1965 value: amount,1966 },1967 }],1968 true, 1969 );1970 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1971 }19721973 19741975197619771978197919801981 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, tokens: {value: bigint}[], owner: ICrossAccountId): Promise<boolean> {1982 const rawTokens = [];1983 for (const token of tokens) {1984 const raw = {Fungible: {Value: token.value}};1985 rawTokens.push(raw);1986 }1987 const creationResult = await this.helper.executeExtrinsic(1988 signer,1989 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1990 true,1991 );1992 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated');1993 }19941995 199619971998199920002001 async getTop10Owners(collectionId: number): Promise<CrossAccountId[]> {2002 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(CrossAccountId.fromLowerCaseKeys);2003 }20042005 2006200720082009201020112012 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {2013 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();2014 }20152016 201720182019202020212022202320242025 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2026 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);2027 }20282029 2030203120322033203420352036203720382039 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {2040 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);2041 }20422043 20442045204620472048204920502051 async burnTokens(signer: IKeyringPair, collectionId: number, amount=1n): Promise<boolean> {2052 return await super.burnToken(signer, collectionId, 0, amount);2053 }20542055 205620572058205920602061206220632064 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {2065 return await super.burnTokenFrom(signer, collectionId, 0, fromAddressObj, amount);2066 }20672068 20692070207120722073 async getTotalPieces(collectionId: number): Promise<bigint> {2074 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();2075 }20762077 2078207920802081208220832084208520862087 async approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=1n) {2088 return super.approveToken(signer, collectionId, 0, toAddressObj, amount);2089 }20902091 2092209320942095209620972098 async getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2099 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);2100 }2101}210221032104class ChainGroup extends HelperGroup<ChainHelperBase> {2105 21062107210821092110 getChainProperties(): IChainProperties {2111 const properties = (this.helper.getApi() as any).registry.getChainProperties().toJSON();2112 return {2113 ss58Format: properties.ss58Format.toJSON(),2114 tokenDecimals: properties.tokenDecimals.toJSON(),2115 tokenSymbol: properties.tokenSymbol.toJSON(),2116 };2117 }21182119 21202121212221232124 async getLatestBlockNumber(): Promise<number> {2125 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();2126 }21272128 212921302131213221332134 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {2135 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();2136 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;2137 return blockHash;2138 }21392140 2141 async getBlock(blockHashOrNumber: string | number): Promise<IBlock | null> {2142 const blockHash = typeof blockHashOrNumber === 'string' ? blockHashOrNumber : await this.getBlockHashByNumber(blockHashOrNumber);2143 if (!blockHash) return null;2144 return (await this.helper.callRpc('api.rpc.chain.getBlock', [blockHash])).toHuman().block;2145 }21462147 214821492150215121522153 async getNonce(address: TSubstrateAccount): Promise<number> {2154 return (await this.helper.callRpc('api.query.system.account', [address])).nonce.toNumber();2155 }2156}21572158class SubstrateBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2159 216021612162216321642165 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2166 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();2167 }21682169 21702171217221732174217521762177 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2178 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);21792180 let transfer = {from: null, to: null, amount: 0n} as any;2181 result.result.events.forEach(({event: {data, method, section}}) => {2182 if ((section === 'balances') && (method === 'Transfer')) {2183 transfer = {2184 from: this.helper.address.normalizeSubstrate(data[0]),2185 to: this.helper.address.normalizeSubstrate(data[1]),2186 amount: BigInt(data[2]),2187 };2188 }2189 });2190 const isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from2191 && this.helper.address.normalizeSubstrate(address) === transfer.to2192 && BigInt(amount) === transfer.amount;2193 return isSuccess;2194 }21952196 21972198219922002201 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2202 const accountInfo = (await this.helper.callRpc('api.query.system.account', [address])).data;2203 return {free: accountInfo.free.toBigInt(), miscFrozen: accountInfo.miscFrozen.toBigInt(), feeFrozen: accountInfo.feeFrozen.toBigInt(), reserved: accountInfo.reserved.toBigInt()};2204 }2205}22062207class EthereumBalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2208 220922102211221222132214 async getEthereum(address: TEthereumAccount): Promise<bigint> {2215 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();2216 }22172218 22192220222122222223222422252226 async transferToEthereum(signer: TSigner, address: TEthereumAccount, amount: bigint | string): Promise<boolean> {2227 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true);22282229 let transfer = {from: null, to: null, amount: 0n} as any;2230 result.result.events.forEach(({event: {data, method, section}}) => {2231 if ((section === 'balances') && (method === 'Transfer')) {2232 transfer = {2233 from: data[0].toString(),2234 to: data[1].toString(),2235 amount: BigInt(data[2]),2236 };2237 }2238 });2239 const isSuccess = (typeof signer === 'string' ? signer : signer.address) === transfer.from2240 && address === transfer.to2241 && BigInt(amount) === transfer.amount;2242 return isSuccess;2243 }2244}22452246class BalanceGroup<T extends ChainHelperBase> extends HelperGroup<T> {2247 subBalanceGroup: SubstrateBalanceGroup<T>;2248 ethBalanceGroup: EthereumBalanceGroup<T>;22492250 constructor(helper: T) {2251 super(helper);2252 this.subBalanceGroup = new SubstrateBalanceGroup(helper);2253 this.ethBalanceGroup = new EthereumBalanceGroup(helper);2254 }22552256 getCollectionCreationPrice(): bigint {2257 return 2n * this.getOneTokenNominal();2258 }2259 22602261226222632264 getOneTokenNominal(): bigint {2265 const chainProperties = this.helper.chain.getChainProperties();2266 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);2267 }22682269 227022712272227322742275 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {2276 return this.subBalanceGroup.getSubstrate(address);2277 }22782279 22802281228222832284 async getSubstrateFull(address: TSubstrateAccount): Promise<ISubstrateBalance> {2285 return this.subBalanceGroup.getSubstrateFull(address);2286 }22872288 228922902291229222932294 async getEthereum(address: TEthereumAccount): Promise<bigint> {2295 return this.ethBalanceGroup.getEthereum(address);2296 }22972298 22992300230123022303230423052306 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2307 return this.subBalanceGroup.transferToSubstrate(signer, address, amount);2308 }23092310 async forceTransferToSubstrate(signer: TSigner, from: TSubstrateAccount, to: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2311 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.forceTransfer', [from, to, amount], true);23122313 let transfer = {from: null, to: null, amount: 0n} as any;2314 result.result.events.forEach(({event: {data, method, section}}) => {2315 if ((section === 'balances') && (method === 'Transfer')) {2316 transfer = {2317 from: this.helper.address.normalizeSubstrate(data[0]),2318 to: this.helper.address.normalizeSubstrate(data[1]),2319 amount: BigInt(data[2]),2320 };2321 }2322 });2323 let isSuccess = this.helper.address.normalizeSubstrate(from) === transfer.from;2324 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(to) === transfer.to;2325 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2326 return isSuccess;2327 }2328}23292330class AddressGroup extends HelperGroup<ChainHelperBase> {2331 2332233323342335233623372338 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2339 return CrossAccountId.normalizeSubstrateAddress(address, ss58Format);2340 }23412342 234323442345234623472348 normalizeSubstrateToChainFormat(address: TSubstrateAccount): TSubstrateAccount {2349 return this.normalizeSubstrate(address, this.helper.chain.getChainProperties().ss58Format);2350 }23512352 2353235423552356235723582359 ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): TSubstrateAccount {2360 return CrossAccountId.translateEthToSub(ethAddress, toChainFormat ? this.helper.chain.getChainProperties().ss58Format : undefined);2361 }23622363 236423652366236723682369 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2370 return CrossAccountId.translateSubToEth(subAddress);2371 }23722373 237423752376237723782379 encodeSubstrateAddress (key: Uint8Array | string | bigint, ss58Format = 42): string {2380 const u8a :Uint8Array = typeof key === 'string'2381 ? hexToU8a(key)2382 : typeof key === 'bigint'2383 ? hexToU8a(key.toString(16))2384 : key;2385 2386 if (ss58Format < 0 || ss58Format > 16383 || [46, 47].includes(ss58Format)) {2387 throw new Error(`ss58Format is not valid, received ${typeof ss58Format} "${ss58Format}"`);2388 }2389 2390 const allowedDecodedLengths = [1, 2, 4, 8, 32, 33];2391 if (!allowedDecodedLengths.includes(u8a.length)) {2392 throw new Error(`key length is not valid, received ${u8a.length}, valid values are ${allowedDecodedLengths.join(', ')}`);2393 }2394 2395 const u8aPrefix = ss58Format < 642396 ? new Uint8Array([ss58Format])2397 : new Uint8Array([2398 ((ss58Format & 0xfc) >> 2) | 0x40,2399 (ss58Format >> 8) | ((ss58Format & 0x03) << 6),2400 ]);24012402 const input = u8aConcat(u8aPrefix, u8a);2403 2404 return base58Encode(u8aConcat(2405 input,2406 blake2AsU8a(input).subarray(0, [32, 33].includes(u8a.length) ? 2 : 1),2407 ));2408 }24092410 24112412241324142415 restoreCrossAccountFromBigInt(number: bigint): TSubstrateAccount {2416 if (this.helper.api === null) {2417 throw 'Not connected';2418 }2419 const res = this.helper.api.registry.createType('AccountId', '0x' + number.toString(16).padStart(64, '0')).toJSON();2420 if (res === undefined || res === null) {2421 throw 'Restore address error';2422 }2423 return res.toString();2424 }24252426 24272428242924302431 convertCrossAccountFromEthCrossAcoount(ethCrossAccount: IEthCrossAccountId): ICrossAccountId {2432 if (ethCrossAccount.field_1 === '0') {2433 return {Ethereum: ethCrossAccount.field_0.toLocaleLowerCase()};2434 }2435 2436 const ss58 = this.restoreCrossAccountFromBigInt(BigInt(ethCrossAccount.field_1));2437 return {Substrate: ss58};2438 }24392440 paraSiblingSovereignAccount(paraid: number) {2441 2442 2443 const siblingPrefix = '0x7369626c';24442445 const encodedParaId = this.helper.getApi().createType('u32', paraid).toHex(true).substring(2);2446 const suffix = '000000000000000000000000000000000000000000000000';24472448 return siblingPrefix + encodedParaId + suffix;2449 }2450}24512452class StakingGroup extends HelperGroup<UniqueHelper> {2453 2454245524562457245824592460 async stake(signer: TSigner, amountToStake: bigint, label?: string): Promise<boolean> {2461 if(typeof label === 'undefined') label = `${signer.address} amount: ${amountToStake}`;2462 const _stakeResult = await this.helper.executeExtrinsic(2463 signer, 'api.tx.appPromotion.stake',2464 [amountToStake], true,2465 );2466 2467 return true;2468 }24692470 2471247224732474247524762477 async unstake(signer: TSigner, label?: string): Promise<number> {2478 if(typeof label === 'undefined') label = `${signer.address}`;2479 const _unstakeResult = await this.helper.executeExtrinsic(2480 signer, 'api.tx.appPromotion.unstake',2481 [], true,2482 );2483 2484 return 1;2485 }24862487 24882489249024912492 async getTotalStaked(address?: ICrossAccountId): Promise<bigint> {2493 if (address) return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked', [address])).toBigInt();2494 return (await this.helper.callRpc('api.rpc.appPromotion.totalStaked')).toBigInt();2495 }24962497 24982499250025012502 async getTotalStakedPerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2503 const rawTotalStakerdPerBlock = await this.helper.callRpc('api.rpc.appPromotion.totalStakedPerBlock', [address]);2504 return rawTotalStakerdPerBlock.map(([block, amount]: any[]) => {2505 return {2506 block: block.toBigInt(),2507 amount: amount.toBigInt(),2508 };2509 });2510 }25112512 25132514251525162517 async getPendingUnstake(address: ICrossAccountId): Promise<bigint> {2518 return (await this.helper.callRpc('api.rpc.appPromotion.pendingUnstake', [address])).toBigInt();2519 }25202521 25222523252425252526 async getPendingUnstakePerBlock(address: ICrossAccountId): Promise<IStakingInfo[]> {2527 const rawUnstakedPerBlock = await this.helper.callRpc('api.rpc.appPromotion.pendingUnstakePerBlock', [address]);2528 const result = rawUnstakedPerBlock.map(([block, amount]: any[]) => {2529 return {2530 block: block.toBigInt(),2531 amount: amount.toBigInt(),2532 };2533 });2534 return result;2535 }2536}25372538class SchedulerGroup extends HelperGroup<UniqueHelper> {2539 constructor(helper: UniqueHelper) {2540 super(helper);2541 }25422543 async cancelScheduled(signer: TSigner, scheduledId: string) {2544 return this.helper.executeExtrinsic(2545 signer,2546 'api.tx.scheduler.cancelNamed',2547 [scheduledId],2548 true,2549 );2550 }25512552 async changePriority(signer: TSigner, scheduledId: string, priority: number) {2553 return this.helper.executeExtrinsic(2554 signer,2555 'api.tx.scheduler.changeNamedPriority',2556 [scheduledId, priority],2557 true,2558 );2559 }25602561 scheduleAt<T extends UniqueHelper>(2562 scheduledId: string,2563 executionBlockNumber: number,2564 options: ISchedulerOptions = {},2565 ) {2566 return this.schedule<T>('scheduleNamed', scheduledId, executionBlockNumber, options);2567 }25682569 scheduleAfter<T extends UniqueHelper>(2570 scheduledId: string,2571 blocksBeforeExecution: number,2572 options: ISchedulerOptions = {},2573 ) {2574 return this.schedule<T>('scheduleNamedAfter', scheduledId, blocksBeforeExecution, options);2575 }25762577 schedule<T extends UniqueHelper>(2578 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter',2579 scheduledId: string,2580 blocksNum: number,2581 options: ISchedulerOptions = {},2582 ) {2583 2584 const ScheduledHelperType = ScheduledUniqueHelper(this.helper.helperBase);2585 return this.helper.clone(ScheduledHelperType, {2586 scheduleFn,2587 scheduledId,2588 blocksNum,2589 options,2590 }) as T;2591 }2592}25932594class ForeignAssetsGroup extends HelperGroup<UniqueHelper> {2595 async register(signer: TSigner, ownerAddress: TSubstrateAccount, location: any, metadata: IForeignAssetMetadata) {2596 await this.helper.executeExtrinsic(2597 signer,2598 'api.tx.foreignAssets.registerForeignAsset',2599 [ownerAddress, location, metadata],2600 true,2601 );2602 }26032604 async update(signer: TSigner, foreignAssetId: number, location: any, metadata: IForeignAssetMetadata) {2605 await this.helper.executeExtrinsic(2606 signer,2607 'api.tx.foreignAssets.updateForeignAsset',2608 [foreignAssetId, location, metadata],2609 true,2610 );2611 }2612}26132614class XcmGroup<T extends ChainHelperBase> extends HelperGroup<T> {2615 palletName: string;26162617 constructor(helper: T, palletName: string) {2618 super(helper);26192620 this.palletName = palletName;2621 }26222623 async limitedReserveTransferAssets(signer: TSigner, destination: any, beneficiary: any, assets: any, feeAssetItem: number, weightLimit: number) {2624 await this.helper.executeExtrinsic(signer, `api.tx.${this.palletName}.limitedReserveTransferAssets`, [destination, beneficiary, assets, feeAssetItem, {Limited: weightLimit}], true);2625 }2626}26272628class XTokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2629 async transfer(signer: TSigner, currencyId: any, amount: bigint, destination: any, destWeight: number) {2630 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transfer', [currencyId, amount, destination, destWeight], true);2631 }26322633 async transferMultiasset(signer: TSigner, asset: any, destination: any, destWeight: number) {2634 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMultiasset', [asset, destination, destWeight], true);2635 }26362637 async transferMulticurrencies(signer: TSigner, currencies: any[], feeItem: number, destLocation: any, destWeight: number) {2638 await this.helper.executeExtrinsic(signer, 'api.tx.xTokens.transferMulticurrencies', [currencies, feeItem, destLocation, destWeight], true);2639 }2640}26412642class TokensGroup<T extends ChainHelperBase> extends HelperGroup<T> {2643 async accounts(address: string, currencyId: any) {2644 const {free} = (await this.helper.callRpc('api.query.tokens.accounts', [address, currencyId])).toJSON() as any;2645 return BigInt(free);2646 }2647}26482649class AssetsGroup<T extends ChainHelperBase> extends HelperGroup<T> {2650 async create(signer: TSigner, assetId: number, admin: string, minimalBalance: bigint) {2651 await this.helper.executeExtrinsic(signer, 'api.tx.assets.create', [assetId, admin, minimalBalance], true);2652 }26532654 async setMetadata(signer: TSigner, assetId: number, name: string, symbol: string, decimals: number) {2655 await this.helper.executeExtrinsic(signer, 'api.tx.assets.setMetadata', [assetId, name, symbol, decimals], true);2656 }26572658 async mint(signer: TSigner, assetId: number, beneficiary: string, amount: bigint) {2659 await this.helper.executeExtrinsic(signer, 'api.tx.assets.mint', [assetId, beneficiary, amount], true);2660 }26612662 async account(assetId: string | number, address: string) {2663 const accountAsset = (2664 await this.helper.callRpc('api.query.assets.account', [assetId, address])2665 ).toJSON()! as any;26662667 if (accountAsset !== null) {2668 return BigInt(accountAsset['balance']);2669 } else {2670 return null;2671 }2672 }2673}26742675class AcalaAssetRegistryGroup extends HelperGroup<AcalaHelper> {2676 async registerForeignAsset(signer: TSigner, destination: any, metadata: AcalaAssetMetadata) {2677 await this.helper.executeExtrinsic(signer, 'api.tx.assetRegistry.registerForeignAsset', [destination, metadata], true);2678 }2679}26802681class MoonbeamAssetManagerGroup extends HelperGroup<MoonbeamHelper> {2682 makeRegisterForeignAssetProposal(assetInfo: MoonbeamAssetInfo) {2683 const apiPrefix = 'api.tx.assetManager.';26842685 const registerTx = this.helper.constructApiCall(2686 apiPrefix + 'registerForeignAsset',2687 [assetInfo.location, assetInfo.metadata, assetInfo.existentialDeposit, assetInfo.isSufficient],2688 );26892690 const setUnitsTx = this.helper.constructApiCall(2691 apiPrefix + 'setAssetUnitsPerSecond',2692 [assetInfo.location, assetInfo.unitsPerSecond, assetInfo.numAssetsWeightHint],2693 );26942695 const batchCall = this.helper.getApi().tx.utility.batchAll([registerTx, setUnitsTx]);2696 const encodedProposal = batchCall?.method.toHex() || '';2697 return encodedProposal;2698 }26992700 async assetTypeId(location: any) {2701 return await this.helper.callRpc('api.query.assetManager.assetTypeId', [location]);2702 }2703}27042705class MoonbeamDemocracyGroup extends HelperGroup<MoonbeamHelper> {2706 async notePreimage(signer: TSigner, encodedProposal: string) {2707 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.notePreimage', [encodedProposal], true);2708 }27092710 externalProposeMajority(proposalHash: string) {2711 return this.helper.constructApiCall('api.tx.democracy.externalProposeMajority', [proposalHash]);2712 }27132714 fastTrack(proposalHash: string, votingPeriod: number, delayPeriod: number) {2715 return this.helper.constructApiCall('api.tx.democracy.fastTrack', [proposalHash, votingPeriod, delayPeriod]);2716 }27172718 async referendumVote(signer: TSigner, referendumIndex: number, accountVote: DemocracyStandardAccountVote) {2719 await this.helper.executeExtrinsic(signer, 'api.tx.democracy.vote', [referendumIndex, {Standard: accountVote}], true);2720 }2721}27222723class MoonbeamCollectiveGroup extends HelperGroup<MoonbeamHelper> {2724 collective: string;27252726 constructor(helper: MoonbeamHelper, collective: string) {2727 super(helper);27282729 this.collective = collective;2730 }27312732 async propose(signer: TSigner, threshold: number, proposalHash: string, lengthBound: number) {2733 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.propose`, [threshold, proposalHash, lengthBound], true);2734 }27352736 async vote(signer: TSigner, proposalHash: string, proposalIndex: number, approve: boolean) {2737 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.vote`, [proposalHash, proposalIndex, approve], true);2738 }27392740 async close(signer: TSigner, proposalHash: string, proposalIndex: number, weightBound: number, lengthBound: number) {2741 await this.helper.executeExtrinsic(signer, `api.tx.${this.collective}.close`, [proposalHash, proposalIndex, weightBound, lengthBound], true);2742 }27432744 async proposalCount() {2745 return Number(await this.helper.callRpc(`api.query.${this.collective}.proposalCount`, []));2746 }2747}27482749export type ChainHelperBaseConstructor = new(...args: any[]) => ChainHelperBase;2750export type UniqueHelperConstructor = new(...args: any[]) => UniqueHelper;27512752export class UniqueHelper extends ChainHelperBase {2753 balance: BalanceGroup<UniqueHelper>;2754 collection: CollectionGroup;2755 nft: NFTGroup;2756 rft: RFTGroup;2757 ft: FTGroup;2758 staking: StakingGroup;2759 scheduler: SchedulerGroup;2760 foreignAssets: ForeignAssetsGroup;2761 xcm: XcmGroup<UniqueHelper>;2762 xTokens: XTokensGroup<UniqueHelper>;2763 tokens: TokensGroup<UniqueHelper>;27642765 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2766 super(logger, options.helperBase ?? UniqueHelper);27672768 this.balance = new BalanceGroup(this);2769 this.collection = new CollectionGroup(this);2770 this.nft = new NFTGroup(this);2771 this.rft = new RFTGroup(this);2772 this.ft = new FTGroup(this);2773 this.staking = new StakingGroup(this);2774 this.scheduler = new SchedulerGroup(this);2775 this.foreignAssets = new ForeignAssetsGroup(this);2776 this.xcm = new XcmGroup(this, 'polkadotXcm');2777 this.xTokens = new XTokensGroup(this);2778 this.tokens = new TokensGroup(this);2779 }27802781 getSudo<T extends UniqueHelper>() {2782 2783 const SudoHelperType = SudoHelper(this.helperBase);2784 return this.clone(SudoHelperType) as T;2785 }2786}27872788export class XcmChainHelper extends ChainHelperBase {2789 async connect(wsEndpoint: string, _listeners?: any): Promise<void> {2790 const wsProvider = new WsProvider(wsEndpoint);2791 this.api = new ApiPromise({2792 provider: wsProvider,2793 });2794 await this.api.isReadyOrError;2795 this.network = await UniqueHelper.detectNetwork(this.api);2796 }2797}27982799export class RelayHelper extends XcmChainHelper {2800 xcm: XcmGroup<RelayHelper>;28012802 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2803 super(logger, options.helperBase ?? RelayHelper);28042805 this.xcm = new XcmGroup(this, 'xcmPallet');2806 }2807}28082809export class WestmintHelper extends XcmChainHelper {2810 balance: SubstrateBalanceGroup<WestmintHelper>;2811 xcm: XcmGroup<WestmintHelper>;2812 assets: AssetsGroup<WestmintHelper>;2813 xTokens: XTokensGroup<WestmintHelper>;28142815 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2816 super(logger, options.helperBase ?? WestmintHelper);28172818 this.balance = new SubstrateBalanceGroup(this);2819 this.xcm = new XcmGroup(this, 'polkadotXcm');2820 this.assets = new AssetsGroup(this);2821 this.xTokens = new XTokensGroup(this);2822 }2823}28242825export class MoonbeamHelper extends XcmChainHelper {2826 balance: EthereumBalanceGroup<MoonbeamHelper>;2827 assetManager: MoonbeamAssetManagerGroup;2828 assets: AssetsGroup<MoonbeamHelper>;2829 xTokens: XTokensGroup<MoonbeamHelper>;2830 democracy: MoonbeamDemocracyGroup;2831 collective: {2832 council: MoonbeamCollectiveGroup,2833 techCommittee: MoonbeamCollectiveGroup,2834 };28352836 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2837 super(logger, options.helperBase ?? MoonbeamHelper);28382839 this.balance = new EthereumBalanceGroup(this);2840 this.assetManager = new MoonbeamAssetManagerGroup(this);2841 this.assets = new AssetsGroup(this);2842 this.xTokens = new XTokensGroup(this);2843 this.democracy = new MoonbeamDemocracyGroup(this);2844 this.collective = {2845 council: new MoonbeamCollectiveGroup(this, 'councilCollective'),2846 techCommittee: new MoonbeamCollectiveGroup(this, 'techCommitteeCollective'),2847 };2848 }2849}28502851export class AcalaHelper extends XcmChainHelper {2852 balance: SubstrateBalanceGroup<AcalaHelper>;2853 assetRegistry: AcalaAssetRegistryGroup;2854 xTokens: XTokensGroup<AcalaHelper>;2855 tokens: TokensGroup<AcalaHelper>;28562857 constructor(logger?: ILogger, options: {[key: string]: any} = {}) {2858 super(logger, options.helperBase ?? AcalaHelper);28592860 this.balance = new SubstrateBalanceGroup(this);2861 this.assetRegistry = new AcalaAssetRegistryGroup(this);2862 this.xTokens = new XTokensGroup(this);2863 this.tokens = new TokensGroup(this);2864 }28652866 getSudo<T extends AcalaHelper>() {2867 2868 const SudoHelperType = SudoHelper(this.helperBase);2869 return this.clone(SudoHelperType) as T;2870 }2871}287228732874function ScheduledUniqueHelper<T extends UniqueHelperConstructor>(Base: T) {2875 return class extends Base {2876 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter';2877 scheduledId: string;2878 blocksNum: number;2879 options: ISchedulerOptions;28802881 constructor(...args: any[]) {2882 const logger = args[0] as ILogger;2883 const options = args[1] as {2884 scheduleFn: 'scheduleNamed' | 'scheduleNamedAfter',2885 scheduledId: string,2886 blocksNum: number,2887 options: ISchedulerOptions2888 };28892890 super(logger);28912892 this.scheduleFn = options.scheduleFn;2893 this.scheduledId = options.scheduledId;2894 this.blocksNum = options.blocksNum;2895 this.options = options.options;2896 }28972898 executeExtrinsic(sender: IKeyringPair, scheduledExtrinsic: string, scheduledParams: any[], expectSuccess?: boolean): Promise<ITransactionResult> {2899 const scheduledTx = this.constructApiCall(scheduledExtrinsic, scheduledParams);2900 const extrinsic = 'api.tx.scheduler.' + this.scheduleFn;29012902 return super.executeExtrinsic(2903 sender,2904 extrinsic,2905 [2906 this.scheduledId,2907 this.blocksNum,2908 this.options.periodic ? [this.options.periodic.period, this.options.periodic.repetitions] : null,2909 this.options.priority ?? null,2910 scheduledTx,2911 ],2912 expectSuccess,2913 );2914 }2915 };2916}291729182919function SudoHelper<T extends ChainHelperBaseConstructor>(Base: T) {2920 return class extends Base {2921 constructor(...args: any[]) {2922 super(...args);2923 }29242925 executeExtrinsic (2926 sender: IKeyringPair,2927 extrinsic: string,2928 params: any[],2929 expectSuccess?: boolean,2930 ): Promise<ITransactionResult> {2931 const call = this.constructApiCall(extrinsic, params);2932 return super.executeExtrinsic(2933 sender,2934 'api.tx.sudo.sudo',2935 [call],2936 expectSuccess,2937 );2938 }2939 };2940}29412942export class UniqueBaseCollection {2943 helper: UniqueHelper;2944 collectionId: number;29452946 constructor(collectionId: number, uniqueHelper: UniqueHelper) {2947 this.collectionId = collectionId;2948 this.helper = uniqueHelper;2949 }29502951 async getData() {2952 return await this.helper.collection.getData(this.collectionId);2953 }29542955 async getLastTokenId() {2956 return await this.helper.collection.getLastTokenId(this.collectionId);2957 }29582959 async doesTokenExist(tokenId: number) {2960 return await this.helper.collection.doesTokenExist(this.collectionId, tokenId);2961 }29622963 async getAdmins() {2964 return await this.helper.collection.getAdmins(this.collectionId);2965 }29662967 async getAllowList() {2968 return await this.helper.collection.getAllowList(this.collectionId);2969 }29702971 async getEffectiveLimits() {2972 return await this.helper.collection.getEffectiveLimits(this.collectionId);2973 }29742975 async getProperties(propertyKeys?: string[] | null) {2976 return await this.helper.collection.getProperties(this.collectionId, propertyKeys);2977 }29782979 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {2980 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);2981 }29822983 async getOptions() {2984 return await this.helper.collection.getCollectionOptions(this.collectionId);2985 }29862987 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount) {2988 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress);2989 }29902991 async confirmSponsorship(signer: TSigner) {2992 return await this.helper.collection.confirmSponsorship(signer, this.collectionId);2993 }29942995 async removeSponsor(signer: TSigner) {2996 return await this.helper.collection.removeSponsor(signer, this.collectionId);2997 }29982999 async setLimits(signer: TSigner, limits: ICollectionLimits) {3000 return await this.helper.collection.setLimits(signer, this.collectionId, limits);3001 }30023003 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount) {3004 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress);3005 }30063007 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3008 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj);3009 }30103011 async addToAllowList(signer: TSigner, addressObj: ICrossAccountId) {3012 return await this.helper.collection.addToAllowList(signer, this.collectionId, addressObj);3013 }30143015 async removeFromAllowList(signer: TSigner, addressObj: ICrossAccountId) {3016 return await this.helper.collection.removeFromAllowList(signer, this.collectionId, addressObj);3017 }30183019 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId) {3020 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj);3021 }30223023 async setProperties(signer: TSigner, properties: IProperty[]) {3024 return await this.helper.collection.setProperties(signer, this.collectionId, properties);3025 }30263027 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3028 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys);3029 }30303031 async setPermissions(signer: TSigner, permissions: ICollectionPermissions) {3032 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions);3033 }30343035 async enableNesting(signer: TSigner, permissions: INestingPermissions) {3036 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions);3037 }30383039 async disableNesting(signer: TSigner) {3040 return await this.helper.collection.disableNesting(signer, this.collectionId);3041 }30423043 async burn(signer: TSigner) {3044 return await this.helper.collection.burn(signer, this.collectionId);3045 }30463047 scheduleAt<T extends UniqueHelper>(3048 scheduledId: string,3049 executionBlockNumber: number,3050 options: ISchedulerOptions = {},3051 ) {3052 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3053 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3054 }30553056 scheduleAfter<T extends UniqueHelper>(3057 scheduledId: string,3058 blocksBeforeExecution: number,3059 options: ISchedulerOptions = {},3060 ) {3061 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3062 return new UniqueBaseCollection(this.collectionId, scheduledHelper);3063 }30643065 getSudo<T extends UniqueHelper>() {3066 return new UniqueBaseCollection(this.collectionId, this.helper.getSudo<T>());3067 }3068}306930703071export class UniqueNFTCollection extends UniqueBaseCollection {3072 getTokenObject(tokenId: number) {3073 return new UniqueNFToken(tokenId, this);3074 }30753076 async getTokensByAddress(addressObj: ICrossAccountId) {3077 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);3078 }30793080 async getToken(tokenId: number, blockHashAt?: string) {3081 return await this.helper.nft.getToken(this.collectionId, tokenId, [], blockHashAt);3082 }30833084 async getTokenOwner(tokenId: number, blockHashAt?: string) {3085 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);3086 }30873088 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {3089 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);3090 }30913092 async getTokenChildren(tokenId: number, blockHashAt?: string) {3093 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);3094 }30953096 async getPropertyPermissions(propertyKeys: string[] | null = null) {3097 return await this.helper.nft.getPropertyPermissions(this.collectionId, propertyKeys);3098 }30993100 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3101 return await this.helper.nft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3102 }31033104 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {3105 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);3106 }31073108 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3109 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);3110 }31113112 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId) {3113 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj);3114 }31153116 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {3117 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);3118 }31193120 async mintToken(signer: TSigner, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3121 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties});3122 }31233124 async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[]) {3125 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens);3126 }31273128 async burnToken(signer: TSigner, tokenId: number) {3129 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId);3130 }31313132 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId) {3133 return await this.helper.nft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj);3134 }31353136 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3137 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties);3138 }31393140 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3141 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3142 }31433144 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3145 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3146 }31473148 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken) {3149 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj);3150 }31513152 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3153 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj);3154 }31553156 scheduleAt<T extends UniqueHelper>(3157 scheduledId: string,3158 executionBlockNumber: number,3159 options: ISchedulerOptions = {},3160 ) {3161 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3162 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3163 }31643165 scheduleAfter<T extends UniqueHelper>(3166 scheduledId: string,3167 blocksBeforeExecution: number,3168 options: ISchedulerOptions = {},3169 ) {3170 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3171 return new UniqueNFTCollection(this.collectionId, scheduledHelper);3172 }31733174 getSudo<T extends UniqueHelper>() {3175 return new UniqueNFTCollection(this.collectionId, this.helper.getSudo<T>());3176 }3177}317831793180export class UniqueRFTCollection extends UniqueBaseCollection {3181 getTokenObject(tokenId: number) {3182 return new UniqueRFToken(tokenId, this);3183 }31843185 async getToken(tokenId: number, blockHashAt?: string) {3186 return await this.helper.rft.getToken(this.collectionId, tokenId, [], blockHashAt);3187 }31883189 async getTokensByAddress(addressObj: ICrossAccountId) {3190 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);3191 }31923193 async getTop10TokenOwners(tokenId: number) {3194 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);3195 }31963197 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {3198 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);3199 }32003201 async getTokenTotalPieces(tokenId: number) {3202 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);3203 }32043205 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3206 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);3207 }32083209 async getPropertyPermissions(propertyKeys: string[] | null = null) {3210 return await this.helper.rft.getPropertyPermissions(this.collectionId, propertyKeys);3211 }32123213 async getTokenProperties(tokenId: number, propertyKeys?: string[] | null) {3214 return await this.helper.rft.getTokenProperties(this.collectionId, tokenId, propertyKeys);3215 }32163217 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=1n) {3218 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);3219 }32203221 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3222 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);3223 }32243225 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=1n) {3226 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, amount);3227 }32283229 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint) {3230 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount);3231 }32323233 async mintToken(signer: TSigner, pieces=1n, owner: ICrossAccountId = {Substrate: signer.address}, properties?: IProperty[]) {3234 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties});3235 }32363237 async mintMultipleTokens(signer: TSigner, tokens: {pieces: bigint, owner: ICrossAccountId, properties?: IProperty[]}[]) {3238 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens);3239 }32403241 async burnToken(signer: TSigner, tokenId: number, amount=1n) {3242 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, amount);3243 }32443245 async burnTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, amount=1n) {3246 return await this.helper.rft.burnTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, amount);3247 }32483249 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[]) {3250 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties);3251 }32523253 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[]) {3254 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys);3255 }32563257 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[]) {3258 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions);3259 }32603261 scheduleAt<T extends UniqueHelper>(3262 scheduledId: string,3263 executionBlockNumber: number,3264 options: ISchedulerOptions = {},3265 ) {3266 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3267 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3268 }32693270 scheduleAfter<T extends UniqueHelper>(3271 scheduledId: string,3272 blocksBeforeExecution: number,3273 options: ISchedulerOptions = {},3274 ) {3275 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3276 return new UniqueRFTCollection(this.collectionId, scheduledHelper);3277 }32783279 getSudo<T extends UniqueHelper>() {3280 return new UniqueRFTCollection(this.collectionId, this.helper.getSudo<T>());3281 }3282}328332843285export class UniqueFTCollection extends UniqueBaseCollection {3286 async getBalance(addressObj: ICrossAccountId) {3287 return await this.helper.ft.getBalance(this.collectionId, addressObj);3288 }32893290 async getTotalPieces() {3291 return await this.helper.ft.getTotalPieces(this.collectionId);3292 }32933294 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3295 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);3296 }32973298 async getTop10Owners() {3299 return await this.helper.ft.getTop10Owners(this.collectionId);3300 }33013302 async mint(signer: TSigner, amount=1n, owner: ICrossAccountId = {Substrate: signer.address}) {3303 return await this.helper.ft.mintTokens(signer, this.collectionId, amount, owner);3304 }33053306 async mintWithOneOwner(signer: TSigner, tokens: {value: bigint}[], owner: ICrossAccountId = {Substrate: signer.address}) {3307 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, tokens, owner);3308 }33093310 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3311 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);3312 }33133314 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3315 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);3316 }33173318 async burnTokens(signer: TSigner, amount=1n) {3319 return await this.helper.ft.burnTokens(signer, this.collectionId, amount);3320 }33213322 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3323 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount);3324 }33253326 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3327 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount);3328 }33293330 scheduleAt<T extends UniqueHelper>(3331 scheduledId: string,3332 executionBlockNumber: number,3333 options: ISchedulerOptions = {},3334 ) {3335 const scheduledHelper = this.helper.scheduler.scheduleAt<T>(scheduledId, executionBlockNumber, options);3336 return new UniqueFTCollection(this.collectionId, scheduledHelper);3337 }33383339 scheduleAfter<T extends UniqueHelper>(3340 scheduledId: string,3341 blocksBeforeExecution: number,3342 options: ISchedulerOptions = {},3343 ) {3344 const scheduledHelper = this.helper.scheduler.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3345 return new UniqueFTCollection(this.collectionId, scheduledHelper);3346 }33473348 getSudo<T extends UniqueHelper>() {3349 return new UniqueFTCollection(this.collectionId, this.helper.getSudo<T>());3350 }3351}335233533354export class UniqueBaseToken {3355 collection: UniqueNFTCollection | UniqueRFTCollection;3356 collectionId: number;3357 tokenId: number;33583359 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {3360 this.collection = collection;3361 this.collectionId = collection.collectionId;3362 this.tokenId = tokenId;3363 }33643365 async getNextSponsored(addressObj: ICrossAccountId) {3366 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);3367 }33683369 async getProperties(propertyKeys?: string[] | null) {3370 return await this.collection.getTokenProperties(this.tokenId, propertyKeys);3371 }33723373 async setProperties(signer: TSigner, properties: IProperty[]) {3374 return await this.collection.setTokenProperties(signer, this.tokenId, properties);3375 }33763377 async deleteProperties(signer: TSigner, propertyKeys: string[]) {3378 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys);3379 }33803381 async doesExist() {3382 return await this.collection.doesTokenExist(this.tokenId);3383 }33843385 nestingAccount() {3386 return this.collection.helper.util.getTokenAccount(this);3387 }33883389 scheduleAt<T extends UniqueHelper>(3390 scheduledId: string,3391 executionBlockNumber: number,3392 options: ISchedulerOptions = {},3393 ) {3394 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3395 return new UniqueBaseToken(this.tokenId, scheduledCollection);3396 }33973398 scheduleAfter<T extends UniqueHelper>(3399 scheduledId: string,3400 blocksBeforeExecution: number,3401 options: ISchedulerOptions = {},3402 ) {3403 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3404 return new UniqueBaseToken(this.tokenId, scheduledCollection);3405 }34063407 getSudo<T extends UniqueHelper>() {3408 return new UniqueBaseToken(this.tokenId, this.collection.getSudo<T>());3409 }3410}341134123413export class UniqueNFToken extends UniqueBaseToken {3414 collection: UniqueNFTCollection;34153416 constructor(tokenId: number, collection: UniqueNFTCollection) {3417 super(tokenId, collection);3418 this.collection = collection;3419 }34203421 async getData(blockHashAt?: string) {3422 return await this.collection.getToken(this.tokenId, blockHashAt);3423 }34243425 async getOwner(blockHashAt?: string) {3426 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);3427 }34283429 async getTopmostOwner(blockHashAt?: string) {3430 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);3431 }34323433 async getChildren(blockHashAt?: string) {3434 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);3435 }34363437 async nest(signer: TSigner, toTokenObj: IToken) {3438 return await this.collection.nestToken(signer, this.tokenId, toTokenObj);3439 }34403441 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId) {3442 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj);3443 }34443445 async transfer(signer: TSigner, addressObj: ICrossAccountId) {3446 return await this.collection.transferToken(signer, this.tokenId, addressObj);3447 }34483449 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {3450 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);3451 }34523453 async approve(signer: TSigner, toAddressObj: ICrossAccountId) {3454 return await this.collection.approveToken(signer, this.tokenId, toAddressObj);3455 }34563457 async isApproved(toAddressObj: ICrossAccountId) {3458 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);3459 }34603461 async burn(signer: TSigner) {3462 return await this.collection.burnToken(signer, this.tokenId);3463 }34643465 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId) {3466 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj);3467 }34683469 scheduleAt<T extends UniqueHelper>(3470 scheduledId: string,3471 executionBlockNumber: number,3472 options: ISchedulerOptions = {},3473 ) {3474 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3475 return new UniqueNFToken(this.tokenId, scheduledCollection);3476 }34773478 scheduleAfter<T extends UniqueHelper>(3479 scheduledId: string,3480 blocksBeforeExecution: number,3481 options: ISchedulerOptions = {},3482 ) {3483 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3484 return new UniqueNFToken(this.tokenId, scheduledCollection);3485 }34863487 getSudo<T extends UniqueHelper>() {3488 return new UniqueNFToken(this.tokenId, this.collection.getSudo<T>());3489 }3490}34913492export class UniqueRFToken extends UniqueBaseToken {3493 collection: UniqueRFTCollection;34943495 constructor(tokenId: number, collection: UniqueRFTCollection) {3496 super(tokenId, collection);3497 this.collection = collection;3498 }34993500 async getData(blockHashAt?: string) {3501 return await this.collection.getToken(this.tokenId, blockHashAt);3502 }35033504 async getTop10Owners() {3505 return await this.collection.getTop10TokenOwners(this.tokenId);3506 }35073508 async getBalance(addressObj: ICrossAccountId) {3509 return await this.collection.getTokenBalance(this.tokenId, addressObj);3510 }35113512 async getTotalPieces() {3513 return await this.collection.getTokenTotalPieces(this.tokenId);3514 }35153516 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {3517 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);3518 }35193520 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=1n) {3521 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);3522 }35233524 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {3525 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);3526 }35273528 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=1n) {3529 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount);3530 }35313532 async repartition(signer: TSigner, amount: bigint) {3533 return await this.collection.repartitionToken(signer, this.tokenId, amount);3534 }35353536 async burn(signer: TSigner, amount=1n) {3537 return await this.collection.burnToken(signer, this.tokenId, amount);3538 }35393540 async burnFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount=1n) {3541 return await this.collection.burnTokenFrom(signer, this.tokenId, fromAddressObj, amount);3542 }35433544 scheduleAt<T extends UniqueHelper>(3545 scheduledId: string,3546 executionBlockNumber: number,3547 options: ISchedulerOptions = {},3548 ) {3549 const scheduledCollection = this.collection.scheduleAt<T>(scheduledId, executionBlockNumber, options);3550 return new UniqueRFToken(this.tokenId, scheduledCollection);3551 }35523553 scheduleAfter<T extends UniqueHelper>(3554 scheduledId: string,3555 blocksBeforeExecution: number,3556 options: ISchedulerOptions = {},3557 ) {3558 const scheduledCollection = this.collection.scheduleAfter<T>(scheduledId, blocksBeforeExecution, options);3559 return new UniqueRFToken(this.tokenId, scheduledCollection);3560 }35613562 getSudo<T extends UniqueHelper>() {3563 return new UniqueRFToken(this.tokenId, this.collection.getSudo<T>());3564 }3565}