12345678import {ApiPromise, WsProvider, Keyring} from '@polkadot/api';9import {ApiInterfaceEvents} from '@polkadot/api/types';10import {IKeyringPair} from '@polkadot/types/types';11import {encodeAddress, decodeAddress, keccakAsHex, evmToAddress, addressToEvm} from '@polkadot/util-crypto';121314const crossAccountIdFromLower = (lowerAddress: ICrossAccountIdLower): ICrossAccountId => {15 const address = {} as ICrossAccountId;16 if(lowerAddress.substrate) address.Substrate = lowerAddress.substrate;17 if(lowerAddress.ethereum) address.Ethereum = lowerAddress.ethereum;18 return address;19};202122const nesting = {23 toChecksumAddress(address: string): string {24 if (typeof address === 'undefined') return '';2526 if(!/^(0x)?[0-9a-f]{40}$/i.test(address)) throw new Error(`Given address "${address}" is not a valid Ethereum address.`);2728 address = address.toLowerCase().replace(/^0x/i,'');29 const addressHash = keccakAsHex(address).replace(/^0x/i,'');30 const checksumAddress = ['0x'];3132 for (let i = 0; i < address.length; i++) {33 34 if (parseInt(addressHash[i], 16) > 7) {35 checksumAddress.push(address[i].toUpperCase());36 } else {37 checksumAddress.push(address[i]);38 }39 }40 return checksumAddress.join('');41 },42 tokenIdToAddress(collectionId: number, tokenId: number) {43 return this.toChecksumAddress(`0xf8238ccfff8ed887463fd5e0${collectionId.toString(16).padStart(8, '0')}${tokenId.toString(16).padStart(8, '0')}`);44 },45};464748interface IChainEvent {49 data: any;50 method: string;51 section: string;52}5354interface ITransactionResult {55 status: 'Fail' | 'Success';56 result: {57 events: {58 event: IChainEvent59 }[];60 },61 moduleError?: string;62}6364interface ILogger {65 log: (msg: any, level?: string) => void;66 level: {67 ERROR: 'ERROR';68 WARNING: 'WARNING';69 INFO: 'INFO';70 [key: string]: string;71 }72}7374interface IUniqueHelperLog {75 executedAt: number;76 executionTime: number;77 type: 'extrinsic' | 'rpc';78 status: 'Fail' | 'Success';79 call: string;80 params: any[];81 moduleError?: string;82 events?: any;83}8485interface IApiListeners {86 connected?: (...args: any[]) => any;87 disconnected?: (...args: any[]) => any;88 error?: (...args: any[]) => any;89 ready?: (...args: any[]) => any; 90 decorated?: (...args: any[]) => any;91}9293interface ICrossAccountId {94 Substrate?: TSubstrateAccount;95 Ethereum?: TEthereumAccount;96}9798interface ICrossAccountIdLower {99 substrate?: TSubstrateAccount;100 ethereum?: TEthereumAccount;101}102103interface ICollectionLimits {104 accountTokenOwnershipLimit?: number | null;105 sponsoredDataSize?: number | null;106 sponsoredDataRateLimit?: {blocks: number} | {sponsoringDisabled: null} | null;107 tokenLimit?: number | null;108 sponsorTransferTimeout?: number | null;109 sponsorApproveTimeout?: number | null;110 ownerCanTransfer?: boolean | null;111 ownerCanDestroy?: boolean | null;112 transfersEnabled?: boolean | null;113}114115interface INestingPermissions {116 tokenOwner?: boolean;117 collectionAdmin?: boolean;118 restricted?: number[] | null;119}120121interface ICollectionPermissions {122 access?: 'Normal' | 'AllowList';123 mintMode?: boolean;124 nesting?: INestingPermissions;125}126127interface IProperty {128 key: string;129 value: string;130}131132interface ITokenPropertyPermission {133 key: string;134 permission: {135 mutable: boolean;136 tokenOwner: boolean;137 collectionAdmin: boolean;138 }139}140141interface IToken {142 collectionId: number;143 tokenId: number;144}145146interface ICollectionCreationOptions {147 name: string | number[];148 description: string | number[];149 tokenPrefix: string | number[];150 mode?: {151 nft?: null;152 refungible?: null;153 fungible?: number;154 }155 permissions?: ICollectionPermissions;156 properties?: IProperty[];157 tokenPropertyPermissions?: ITokenPropertyPermission[];158 limits?: ICollectionLimits;159 pendingSponsor?: TSubstrateAccount;160}161162interface IChainProperties {163 ss58Format: number;164 tokenDecimals: number[];165 tokenSymbol: string[]166}167168type TSubstrateAccount = string;169type TEthereumAccount = string;170type TApiAllowedListeners = 'connected' | 'disconnected' | 'error' | 'ready' | 'decorated';171type TUniqueNetworks = 'opal' | 'quartz' | 'unique';172type TSigner = IKeyringPair; 173174class UniqueUtil {175 static transactionStatus = {176 NOT_READY: 'NotReady',177 FAIL: 'Fail',178 SUCCESS: 'Success',179 };180181 static chainLogType = {182 EXTRINSIC: 'extrinsic',183 RPC: 'rpc',184 };185186 static getNestingTokenAddress(collectionId: number, tokenId: number) {187 return nesting.tokenIdToAddress(collectionId, tokenId);188 }189190 static getDefaultLogger(): ILogger {191 return {192 log(msg: any, level = 'INFO') {193 console[level.toLocaleLowerCase() === 'error' ? 'error' : 'log'](...(Array.isArray(msg) ? msg : [msg]));194 },195 level: {196 ERROR: 'ERROR',197 WARNING: 'WARNING',198 INFO: 'INFO',199 },200 };201 }202203 static vec2str(arr: string[] | number[]) {204 return arr.map(x => String.fromCharCode(parseInt(x.toString()))).join('');205 }206207 static str2vec(string: string) {208 if (typeof string !== 'string') return string;209 return Array.from(string).map(x => x.charCodeAt(0));210 }211212 static fromSeed(seed: string, ss58Format = 42) {213 const keyring = new Keyring({type: 'sr25519', ss58Format});214 return keyring.addFromUri(seed);215 }216217 static normalizeSubstrateAddress(address: string, ss58Format = 42) {218 return encodeAddress(decodeAddress(address), ss58Format);219 }220221 static extractCollectionIdFromCreationResult(creationResult: ITransactionResult, label = 'new collection') {222 if (creationResult.status !== this.transactionStatus.SUCCESS) {223 throw Error(`Unable to create collection for ${label}`);224 }225226 let collectionId = null;227 creationResult.result.events.forEach(({event: {data, method, section}}) => {228 if ((section === 'common') && (method === 'CollectionCreated')) {229 collectionId = parseInt(data[0].toString(), 10);230 }231 });232233 if (collectionId === null) {234 throw Error(`No CollectionCreated event for ${label}`);235 }236237 return collectionId;238 }239240 static extractTokensFromCreationResult(creationResult: ITransactionResult, label = 'new tokens') {241 if (creationResult.status !== this.transactionStatus.SUCCESS) {242 throw Error(`Unable to create tokens for ${label}`);243 }244 let success = false;245 const tokens = [] as any;246 creationResult.result.events.forEach(({event: {data, method, section}}) => {247 if (method === 'ExtrinsicSuccess') {248 success = true;249 } else if ((section === 'common') && (method === 'ItemCreated')) {250 tokens.push({251 collectionId: parseInt(data[0].toString(), 10),252 tokenId: parseInt(data[1].toString(), 10),253 owner: data[2].toJSON(),254 });255 }256 });257 return {success, tokens};258 }259260 static extractTokensFromBurnResult(burnResult: ITransactionResult, label = 'burned tokens') {261 if (burnResult.status !== this.transactionStatus.SUCCESS) {262 throw Error(`Unable to burn tokens for ${label}`);263 }264 let success = false;265 const tokens = [] as any;266 burnResult.result.events.forEach(({event: {data, method, section}}) => {267 if (method === 'ExtrinsicSuccess') {268 success = true;269 } else if ((section === 'common') && (method === 'ItemDestroyed')) {270 tokens.push({271 collectionId: parseInt(data[0].toString(), 10),272 tokenId: parseInt(data[1].toString(), 10),273 owner: data[2].toJSON(),274 });275 }276 });277 return {success, tokens};278 }279280 static findCollectionInEvents(events: {event: IChainEvent}[], collectionId: number, expectedSection: string, expectedMethod: string, label?: string) {281 let eventId = null;282 events.forEach(({event: {data, method, section}}) => {283 if ((section === expectedSection) && (method === expectedMethod)) {284 eventId = parseInt(data[0].toString(), 10);285 }286 });287288 if (eventId === null) {289 throw Error(`No ${expectedMethod} event for ${label}`);290 }291 return eventId === collectionId;292 }293294 static isTokenTransferSuccess(events: {event: IChainEvent}[], collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n) {295 const normalizeAddress = (address: string | ICrossAccountId) => {296 if(typeof address === 'string') return address;297 const obj = {} as any;298 Object.keys(address).forEach(k => {299 obj[k.toLocaleLowerCase()] = address[k as 'Substrate' | 'Ethereum'];300 });301 if(obj.substrate) return {Substrate: this.normalizeSubstrateAddress(obj.substrate)};302 if(obj.ethereum) return {Ethereum: obj.ethereum.toLocaleLowerCase()};303 return address;304 };305 let transfer = {collectionId: null, tokenId: null, from: null, to: null, amount: 1} as any;306 events.forEach(({event: {data, method, section}}) => {307 if ((section === 'common') && (method === 'Transfer')) {308 const hData = (data as any).toJSON();309 transfer = {310 collectionId: hData[0],311 tokenId: hData[1],312 from: normalizeAddress(hData[2]),313 to: normalizeAddress(hData[3]),314 amount: BigInt(hData[4]),315 };316 }317 });318 let isSuccess = parseInt(collectionId.toString()) === transfer.collectionId && parseInt(tokenId.toString()) === transfer.tokenId;319 isSuccess = isSuccess && JSON.stringify(normalizeAddress(fromAddressObj)) === JSON.stringify(transfer.from);320 isSuccess = isSuccess && JSON.stringify(normalizeAddress(toAddressObj)) === JSON.stringify(transfer.to);321 isSuccess = isSuccess && amount === transfer.amount;322 return isSuccess;323 }324}325326327class ChainHelperBase {328 transactionStatus = UniqueUtil.transactionStatus;329 chainLogType = UniqueUtil.chainLogType;330 util: typeof UniqueUtil;331 logger: ILogger;332 api: ApiPromise | null;333 forcedNetwork: TUniqueNetworks | null;334 network: TUniqueNetworks | null;335 chainLog: IUniqueHelperLog[];336337 constructor(logger?: ILogger) {338 this.util = UniqueUtil;339 if (typeof logger == 'undefined') logger = this.util.getDefaultLogger();340 this.logger = logger;341 this.api = null;342 this.forcedNetwork = null;343 this.network = null;344 this.chainLog = [];345 }346347 clearChainLog(): void {348 this.chainLog = [];349 }350351 forceNetwork(value: TUniqueNetworks): void {352 this.forcedNetwork = value;353 }354355 async connect(wsEndpoint: string, listeners?: IApiListeners) {356 if (this.api !== null) throw Error('Already connected');357 const {api, network} = await ChainHelperBase.createConnection(wsEndpoint, listeners, this.forcedNetwork);358 this.api = api;359 this.network = network;360 }361362 async disconnect() {363 if (this.api === null) return;364 await this.api.disconnect();365 this.api = null;366 this.network = null;367 }368369 static async detectNetwork(api: ApiPromise): Promise<TUniqueNetworks> {370 const spec = (await api.query.system.lastRuntimeUpgrade()).toJSON() as any;371 if(['quartz', 'unique'].indexOf(spec.specName) > -1) return spec.specName;372 return 'opal';373 }374375 static async detectNetworkByWsEndpoint(wsEndpoint: string): Promise<TUniqueNetworks> {376 const api = new ApiPromise({provider: new WsProvider(wsEndpoint)});377 await api.isReady;378379 const network = await this.detectNetwork(api);380381 await api.disconnect();382383 return network;384 }385386 static async createConnection(wsEndpoint: string, listeners?: IApiListeners, network?: TUniqueNetworks | null): Promise<{ 387 api: ApiPromise; 388 network: TUniqueNetworks; 389 }> {390 if(typeof network === 'undefined' || network === null) network = 'opal';391 const supportedRPC = {392 opal: {393 unique: require('@unique-nft/opal-testnet-types/definitions').unique.rpc,394 },395 quartz: {396 unique: require('@unique-nft/quartz-mainnet-types/definitions').unique.rpc,397 },398 unique: {399 unique: require('@unique-nft/unique-mainnet-types/definitions').unique.rpc,400 },401 };402 if(!supportedRPC.hasOwnProperty(network)) network = await this.detectNetworkByWsEndpoint(wsEndpoint);403 const rpc = supportedRPC[network];404405 406 407408 const api = new ApiPromise({provider: new WsProvider(wsEndpoint), rpc});409410 await api.isReadyOrError;411412 if (typeof listeners === 'undefined') listeners = {};413 for (const event of ['connected', 'disconnected', 'error', 'ready', 'decorated']) {414 if (!listeners.hasOwnProperty(event) || typeof listeners[event as TApiAllowedListeners] === 'undefined') continue;415 api.on(event as ApiInterfaceEvents, listeners[event as TApiAllowedListeners] as (...args: any[]) => any);416 }417418 return {api, network};419 }420421 getTransactionStatus(data: {events: {event: IChainEvent}[], status: any}) {422 const {events, status} = data;423 if (status.isReady) {424 return this.transactionStatus.NOT_READY;425 }426 if (status.isBroadcast) {427 return this.transactionStatus.NOT_READY;428 }429 if (status.isInBlock || status.isFinalized) {430 const errors = events.filter(e => e.event.data.method === 'ExtrinsicFailed');431 if (errors.length > 0) {432 return this.transactionStatus.FAIL;433 }434 if (events.filter(e => e.event.data.method === 'ExtrinsicSuccess').length > 0) {435 return this.transactionStatus.SUCCESS;436 }437 }438439 return this.transactionStatus.FAIL;440 }441442 signTransaction(sender: TSigner, transaction: any, label = 'transaction', options = null) {443 const sign = (callback: any) => {444 if(options !== null) return transaction.signAndSend(sender, options, callback);445 return transaction.signAndSend(sender, callback);446 };447 return new Promise(async (resolve, reject) => {448 try {449 const unsub = await sign((result: any) => {450 const status = this.getTransactionStatus(result);451452 if (status === this.transactionStatus.SUCCESS) {453 this.logger.log(`${label} successful`);454 unsub();455 resolve({result, status});456 } else if (status === this.transactionStatus.FAIL) {457 let moduleError = null;458459 if (result.hasOwnProperty('dispatchError')) {460 const dispatchError = result['dispatchError'];461462 if (dispatchError && dispatchError.isModule) {463 const modErr = dispatchError.asModule;464 const errorMeta = dispatchError.registry.findMetaError(modErr);465466 moduleError = `${errorMeta.section}.${errorMeta.name}`;467 }468 else {469 this.logger.log(result, this.logger.level.ERROR);470 }471 }472473 this.logger.log(`Something went wrong with ${label}. Status: ${status}`, this.logger.level.ERROR);474 unsub();475 reject({status, moduleError, result});476 }477 });478 } catch (e) {479 this.logger.log(e, this.logger.level.ERROR);480 reject(e);481 }482 });483 }484485 constructApiCall(apiCall: string, params: any[]) {486 if(!apiCall.startsWith('api.')) throw Error(`Invalid api call: ${apiCall}`);487 let call = this.api as any;488 for(const part of apiCall.slice(4).split('.')) {489 call = call[part];490 }491 return call(...params);492 }493494 async executeExtrinsic(sender: TSigner, extrinsic: string, params: any[], expectSuccess=false, failureMessage='expected success') {495 if(this.api === null) throw Error('API not initialized');496 if(!extrinsic.startsWith('api.tx.')) throw Error(`${extrinsic} is not transaction`);497498 const startTime = (new Date()).getTime();499 let result: ITransactionResult;500 let events = [];501 try {502 result = await this.signTransaction(sender, this.constructApiCall(extrinsic, params), extrinsic) as ITransactionResult;503 events = result.result.events.map((x: any) => x.toHuman());504 }505 catch(e) {506 if(!(e as object).hasOwnProperty('status')) throw e;507 result = e as ITransactionResult;508 }509510 const endTime = (new Date()).getTime();511512 const log = {513 executedAt: endTime,514 executionTime: endTime - startTime,515 type: this.chainLogType.EXTRINSIC,516 status: result.status,517 call: extrinsic,518 params,519 } as IUniqueHelperLog;520521 if(result.status !== this.transactionStatus.SUCCESS && result.moduleError) log.moduleError = result.moduleError;522 if(events.length > 0) log.events = events;523524 this.chainLog.push(log);525526 if(expectSuccess && result.status !== this.transactionStatus.SUCCESS) throw Error(failureMessage);527 return result;528 }529530 async callRpc(rpc: string, params?: any[]) {531 if(typeof params === 'undefined') params = [];532 if(this.api === null) throw Error('API not initialized');533 if(!rpc.startsWith('api.rpc.') && !rpc.startsWith('api.query.')) throw Error(`${rpc} is not RPC call`);534535 const startTime = (new Date()).getTime();536 let result;537 let error = null;538 const log = {539 type: this.chainLogType.RPC,540 call: rpc,541 params,542 } as IUniqueHelperLog;543544 try {545 result = await this.constructApiCall(rpc, params);546 }547 catch(e) {548 error = e;549 }550551 const endTime = (new Date()).getTime();552553 log.executedAt = endTime;554 log.status = (error === null ? this.transactionStatus.SUCCESS : this.transactionStatus.FAIL) as 'Fail' | 'Success';555 log.executionTime = endTime - startTime;556557 this.chainLog.push(log);558559 if(error !== null) throw error;560561 return result;562 }563564 getSignerAddress(signer: IKeyringPair | string): string {565 if(typeof signer === 'string') return signer;566 return signer.address;567 }568}569570571class HelperGroup {572 helper: UniqueHelper;573574 constructor(uniqueHelper: UniqueHelper) {575 this.helper = uniqueHelper;576 }577}578579580class CollectionGroup extends HelperGroup {581 582583584585586587588589590 async getTokenNextSponsored(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<number | null> {591 return (await this.helper.callRpc('api.rpc.unique.nextSponsored', [collectionId, addressObj, tokenId])).toJSON();592 }593594 595596597598599 async getTotalCount(): Promise<number> {600 return (await this.helper.callRpc('api.rpc.unique.collectionStats')).created.toNumber();601 }602603 604605606607608609610 async getData(collectionId: number): Promise<{611 id: number;612 name: string;613 description: string;614 tokensCount: number;615 admins: ICrossAccountId[];616 normalizedOwner: TSubstrateAccount;617 raw: any618 } | null> {619 const collection = await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId]);620 const humanCollection = collection.toHuman(), collectionData = {621 id: collectionId, name: null, description: null, tokensCount: 0, admins: [],622 raw: humanCollection,623 } as any, jsonCollection = collection.toJSON();624 if (humanCollection === null) return null;625 collectionData.raw.limits = jsonCollection.limits;626 collectionData.raw.permissions = jsonCollection.permissions;627 collectionData.normalizedOwner = this.helper.address.normalizeSubstrate(collectionData.raw.owner);628 for (const key of ['name', 'description']) {629 collectionData[key] = this.helper.util.vec2str(humanCollection[key]);630 }631632 collectionData.tokensCount = (['RFT', 'NFT'].includes(humanCollection.mode)) ? await this.helper[humanCollection.mode.toLocaleLowerCase() as 'nft' | 'rft'].getLastTokenId(collectionId) : 0;633 collectionData.admins = await this.getAdmins(collectionId);634635 return collectionData;636 }637638 639640641642643644645 async getAdmins(collectionId: number): Promise<ICrossAccountId[]> {646 const normalized = [];647 for(const admin of (await this.helper.callRpc('api.rpc.unique.adminlist', [collectionId])).toHuman()) {648 if(admin.Substrate) normalized.push({Substrate: this.helper.address.normalizeSubstrate(admin.Substrate)});649 else normalized.push(admin);650 }651 return normalized;652 }653654 655656657658659660661 async getEffectiveLimits(collectionId: number): Promise<ICollectionLimits> {662 return (await this.helper.callRpc('api.rpc.unique.effectiveCollectionLimits', [collectionId])).toJSON();663 }664665 666667668669670671672673674 async burn(signer: TSigner, collectionId: number, label?: string): Promise<boolean> {675 if(typeof label === 'undefined') label = `collection #${collectionId}`;676 const result = await this.helper.executeExtrinsic(677 signer,678 'api.tx.unique.destroyCollection', [collectionId],679 true, `Unable to burn collection for ${label}`,680 );681682 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionDestroyed', label);683 }684685 686687688689690691692693694695 async setSponsor(signer: TSigner, collectionId: number, sponsorAddress: TSubstrateAccount, label?: string): Promise<boolean> {696 if(typeof label === 'undefined') label = `collection #${collectionId}`;697 const result = await this.helper.executeExtrinsic(698 signer,699 'api.tx.unique.setCollectionSponsor', [collectionId, sponsorAddress],700 true, `Unable to set collection sponsor for ${label}`,701 );702703 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionSponsorSet', label);704 }705706 707708709710711712713714715 async confirmSponsorship(signer: TSigner, collectionId: number, label?: string): Promise<boolean> {716 if(typeof label === 'undefined') label = `collection #${collectionId}`;717 const result = await this.helper.executeExtrinsic(718 signer,719 'api.tx.unique.confirmSponsorship', [collectionId],720 true, `Unable to confirm collection sponsorship for ${label}`,721 );722723 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'SponsorshipConfirmed', label);724 }725726 727728729730731732733734735736737738739740741742743744 async setLimits(signer: TSigner, collectionId: number, limits: ICollectionLimits, label?: string): Promise<boolean> {745 if(typeof label === 'undefined') label = `collection #${collectionId}`;746 const result = await this.helper.executeExtrinsic(747 signer,748 'api.tx.unique.setCollectionLimits', [collectionId, limits],749 true, `Unable to set collection limits for ${label}`,750 );751752 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionLimitSet', label);753 }754755 756757758759760761762763764765 async changeOwner(signer: TSigner, collectionId: number, ownerAddress: TSubstrateAccount, label?: string): Promise<boolean> {766 if(typeof label === 'undefined') label = `collection #${collectionId}`;767 const result = await this.helper.executeExtrinsic(768 signer,769 'api.tx.unique.changeCollectionOwner', [collectionId, ownerAddress],770 true, `Unable to change collection owner for ${label}`,771 );772773 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionOwnedChanged', label);774 }775776 777778779780781782783784785786 async addAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId, label?: string): Promise<boolean> {787 if(typeof label === 'undefined') label = `collection #${collectionId}`;788 const result = await this.helper.executeExtrinsic(789 signer,790 'api.tx.unique.addCollectionAdmin', [collectionId, adminAddressObj],791 true, `Unable to add collection admin for ${label}`,792 );793794 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminAdded', label);795 }796797 798799800801802803804805806807 async removeAdmin(signer: TSigner, collectionId: number, adminAddressObj: ICrossAccountId, label?: string): Promise<boolean> {808 if(typeof label === 'undefined') label = `collection #${collectionId}`;809 const result = await this.helper.executeExtrinsic(810 signer,811 'api.tx.unique.removeCollectionAdmin', [collectionId, adminAddressObj],812 true, `Unable to remove collection admin for ${label}`,813 );814815 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionAdminRemoved', label);816 }817818 819820821822823824825826827828 async setPermissions(signer: TSigner, collectionId: number, permissions: ICollectionPermissions, label?: string): Promise<boolean> {829 if(typeof label === 'undefined') label = `collection #${collectionId}`;830 const result = await this.helper.executeExtrinsic(831 signer,832 'api.tx.unique.setCollectionPermissions', [collectionId, permissions],833 true, `Unable to set collection permissions for ${label}`,834 );835836 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'unique', 'CollectionPermissionSet', label);837 }838839 840841842843844845846847848849 async enableNesting(signer: TSigner, collectionId: number, permissions: INestingPermissions, label?: string): Promise<boolean> {850 return await this.setPermissions(signer, collectionId, {nesting: permissions}, label);851 }852853 854855856857858859860861862 async disableNesting(signer: TSigner, collectionId: number, label?: string): Promise<boolean> {863 return await this.setPermissions(signer, collectionId, {nesting: {tokenOwner: false, collectionAdmin: false}}, label);864 }865866 867868869870871872873874875876 async setProperties(signer: TSigner, collectionId: number, properties: IProperty[], label?: string): Promise<boolean> {877 if(typeof label === 'undefined') label = `collection #${collectionId}`;878 const result = await this.helper.executeExtrinsic(879 signer,880 'api.tx.unique.setCollectionProperties', [collectionId, properties],881 true, `Unable to set collection properties for ${label}`,882 );883884 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertySet', label);885 }886887 888889890891892893894895896897 async deleteProperties(signer: TSigner, collectionId: number, propertyKeys: string[], label?: string): Promise<boolean> {898 if(typeof label === 'undefined') label = `collection #${collectionId}`;899 const result = await this.helper.executeExtrinsic(900 signer,901 'api.tx.unique.deleteCollectionProperties', [collectionId, propertyKeys],902 true, `Unable to delete collection properties for ${label}`,903 );904905 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'CollectionPropertyDeleted', label);906 }907908 909910911912913914915916917918919 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=1n): Promise<boolean> {920 const result = await this.helper.executeExtrinsic(921 signer,922 'api.tx.unique.transfer', [addressObj, collectionId, tokenId, amount],923 true, `Unable to transfer token #${tokenId} from collection #${collectionId}`,924 );925926 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, {Substrate: typeof signer === 'string' ? signer : signer.address}, addressObj, amount);927 }928929 930931932933934935936937938939940941942 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=1n): Promise<boolean> {943 const result = await this.helper.executeExtrinsic(944 signer,945 'api.tx.unique.transferFrom', [fromAddressObj, toAddressObj, collectionId, tokenId, amount],946 true, `Unable to transfer token #${tokenId} from collection #${collectionId}`,947 );948 return this.helper.util.isTokenTransferSuccess(result.result.events, collectionId, tokenId, fromAddressObj, toAddressObj, amount);949 }950951 952953954955956957958959960961962963 async burnToken(signer: TSigner, collectionId: number, tokenId: number, label?: string, amount=1n): Promise<{964 success: boolean,965 token: number | null966 }> {967 if(typeof label === 'undefined') label = `collection #${collectionId}`;968 const burnResult = await this.helper.executeExtrinsic(969 signer,970 'api.tx.unique.burnItem', [collectionId, tokenId, amount],971 true, `Unable to burn token for ${label}`,972 );973 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult, label);974 if (burnedTokens.tokens.length > 1) throw Error('Burned multiple tokens');975 return {success: burnedTokens.success, token: burnedTokens.tokens.length > 0 ? burnedTokens.tokens[0] : null};976 }977978 979980981982983984985986987988989990 async burnTokenFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, tokenId: number, label?: string, amount=1n): Promise<boolean> {991 if(typeof label === 'undefined') label = `collection #${collectionId}`;992 const burnResult = await this.helper.executeExtrinsic(993 signer,994 'api.tx.unique.burnFrom', [collectionId, fromAddressObj, tokenId, amount],995 true, `Unable to burn token from for ${label}`,996 );997 const burnedTokens = this.helper.util.extractTokensFromBurnResult(burnResult, label);998 return burnedTokens.success && burnedTokens.tokens.length > 0;999 }10001001 10021003100410051006100710081009101010111012 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, label?: string, amount=1n) {1013 if(typeof label === 'undefined') label = `collection #${collectionId}`;1014 const approveResult = await this.helper.executeExtrinsic(1015 signer, 1016 'api.tx.unique.approve', [toAddressObj, collectionId, tokenId, amount],1017 true, `Unable to approve token for ${label}`,1018 );10191020 return this.helper.util.findCollectionInEvents(approveResult.result.events, collectionId, 'common', 'Approved', label);1021 }10221023 102410251026102710281029103010311032 async getTokenApprovedPieces(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId, fromAccountObj: ICrossAccountId): Promise<bigint> {1033 return (await this.helper.callRpc('api.rpc.unique.allowance', [collectionId, fromAccountObj, toAccountObj, tokenId])).toBigInt();1034 }10351036 103710381039104010411042 async getLastTokenId(collectionId: number): Promise<number> {1043 return (await this.helper.callRpc('api.rpc.unique.lastTokenId', [collectionId])).toNumber();1044 }10451046 1047104810491050105110521053 async isTokenExists(collectionId: number, tokenId: number): Promise<boolean> {1054 return (await this.helper.callRpc('api.rpc.unique.tokenExists', [collectionId, tokenId])).toJSON();1055 }1056}10571058class NFTnRFT extends CollectionGroup {1059 10601061106210631064106510661067 async getTokensByAddress(collectionId: number, addressObj: ICrossAccountId): Promise<number[]> {1068 return (await this.helper.callRpc('api.rpc.unique.accountTokens', [collectionId, addressObj])).toJSON();1069 }10701071 107210731074107510761077107810791080 async getToken(collectionId: number, tokenId: number, blockHashAt?: string, propertyKeys?: string[]): Promise<{1081 properties: IProperty[];1082 owner: ICrossAccountId;1083 normalizedOwner: ICrossAccountId;1084 }| null> {1085 let tokenData;1086 if(typeof blockHashAt === 'undefined') {1087 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId]);1088 }1089 else {1090 if(typeof propertyKeys === 'undefined') {1091 const collection = (await this.helper.callRpc('api.rpc.unique.collectionById', [collectionId])).toHuman();1092 if(!collection) return null;1093 propertyKeys = collection.tokenPropertyPermissions.map((x: ITokenPropertyPermission) => x.key);1094 }1095 tokenData = await this.helper.callRpc('api.rpc.unique.tokenData', [collectionId, tokenId, propertyKeys, blockHashAt]);1096 }1097 tokenData = tokenData.toHuman();1098 if (tokenData === null || tokenData.owner === null) return null;1099 const owner = {} as any;1100 for (const key of Object.keys(tokenData.owner)) {1101 owner[key.toLocaleLowerCase()] = key.toLocaleLowerCase() === 'substrate' ? this.helper.address.normalizeSubstrate(tokenData.owner[key]) : tokenData.owner[key];1102 }1103 tokenData.normalizedOwner = crossAccountIdFromLower(owner);1104 return tokenData;1105 }11061107 11081109111011111112111311141115111611171118 async setTokenPropertyPermissions(signer: TSigner, collectionId: number, permissions: ITokenPropertyPermission[], label?: string): Promise<boolean> {1119 if(typeof label === 'undefined') label = `collection #${collectionId}`;1120 const result = await this.helper.executeExtrinsic(1121 signer,1122 'api.tx.unique.setTokenPropertyPermissions', [collectionId, permissions],1123 true, `Unable to set token property permissions for ${label}`,1124 );11251126 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'PropertyPermissionSet', label);1127 }11281129 1130113111321133113411351136113711381139 async setTokenProperties(signer: TSigner, collectionId: number, tokenId: number, properties: IProperty[], label?: string): Promise<boolean> {1140 if(typeof label === 'undefined') label = `token #${tokenId} from collection #${collectionId}`;1141 const result = await this.helper.executeExtrinsic(1142 signer,1143 'api.tx.unique.setTokenProperties', [collectionId, tokenId, properties],1144 true, `Unable to set token properties for ${label}`,1145 );11461147 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertySet', label);1148 }11491150 1151115211531154115511561157115811591160 async deleteTokenProperties(signer: TSigner, collectionId: number, tokenId: number, propertyKeys: string[], label?: string): Promise<boolean> {1161 if(typeof label === 'undefined') label = `token #${tokenId} from collection #${collectionId}`;1162 const result = await this.helper.executeExtrinsic(1163 signer,1164 'api.tx.unique.deleteTokenProperties', [collectionId, tokenId, propertyKeys],1165 true, `Unable to delete token properties for ${label}`,1166 );11671168 return this.helper.util.findCollectionInEvents(result.result.events, collectionId, 'common', 'TokenPropertyDeleted', label);1169 }11701171 117211731174117511761177117811791180 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, mode: 'NFT' | 'RFT', errorLabel = 'Unable to mint collection'): Promise<UniqueCollectionBase> {1181 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1182 collectionOptions.mode = (mode === 'NFT') ? {nft: null} : {refungible: null};1183 for (const key of ['name', 'description', 'tokenPrefix']) {1184 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);1185 }1186 const creationResult = await this.helper.executeExtrinsic(1187 signer,1188 'api.tx.unique.createCollectionEx', [collectionOptions],1189 true, errorLabel,1190 );1191 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult, errorLabel));1192 }11931194 getCollectionObject(collectionId: number): any {1195 return null;1196 }11971198 getTokenObject(collectionId: number, tokenId: number): any {1199 return null;1200 }1201}120212031204class NFTGroup extends NFTnRFT {1205 120612071208120912101211 getCollectionObject(collectionId: number): UniqueNFTCollection {1212 return new UniqueNFTCollection(collectionId, this.helper);1213 }12141215 1216121712181219122012211222 getTokenObject(collectionId: number, tokenId: number): UniqueNFTToken {1223 return new UniqueNFTToken(tokenId, this.getCollectionObject(collectionId));1224 }12251226 12271228122912301231123212331234 async getTokenOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId> {1235 let owner;1236 if (typeof blockHashAt === 'undefined') {1237 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId]);1238 } else {1239 owner = await this.helper.callRpc('api.rpc.unique.tokenOwner', [collectionId, tokenId, blockHashAt]);1240 }1241 return crossAccountIdFromLower(owner.toJSON());1242 }12431244 1245124612471248124912501251 async isTokenApproved(collectionId: number, tokenId: number, toAccountObj: ICrossAccountId): Promise<boolean> {1252 return (await this.getTokenApprovedPieces(collectionId, tokenId, toAccountObj, await this.getTokenOwner(collectionId, tokenId))) === 1n;1253 }12541255 1256125712581259126012611262126312641265 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<boolean> {1266 return await super.transferToken(signer, collectionId, tokenId, addressObj, 1n);1267 }12681269 127012711272127312741275127612771278127912801281 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId): Promise<boolean> {1282 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, 1n);1283 }12841285 12861287128812891290129112921293 async getTokenTopmostOwner(collectionId: number, tokenId: number, blockHashAt?: string): Promise<ICrossAccountId | null> {1294 let owner;1295 if (typeof blockHashAt === 'undefined') {1296 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId]);1297 } else {1298 owner = await this.helper.callRpc('api.rpc.unique.topmostTokenOwner', [collectionId, tokenId, blockHashAt]);1299 }13001301 if (owner === null) return null;13021303 owner = owner.toHuman();13041305 return owner.Substrate ? {Substrate: this.helper.address.normalizeSubstrate(owner.Substrate)} : owner;1306 }13071308 13091310131113121313131413151316 async getTokenChildren(collectionId: number, tokenId: number, blockHashAt?: string): Promise<IToken[]> {1317 let children;1318 if(typeof blockHashAt === 'undefined') {1319 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId]);1320 } else {1321 children = await this.helper.callRpc('api.rpc.unique.tokenChildren', [collectionId, tokenId, blockHashAt]);1322 }13231324 return children.toJSON().map((x: any) => {1325 return {collectionId: x.collection, tokenId: x.token};1326 });1327 }13281329 133013311332133313341335133613371338 async nestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, label='nest token'): Promise<boolean> {1339 const rootTokenAddress = {Ethereum: this.helper.util.getNestingTokenAddress(rootTokenObj.collectionId, rootTokenObj.tokenId)};1340 const result = await this.transferToken(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress);1341 if(!result) {1342 throw Error(`Unable to nest token for ${label}`);1343 }1344 return result;1345 }13461347 1348134913501351135213531354135513561357 async unnestToken(signer: TSigner, tokenObj: IToken, rootTokenObj: IToken, toAddressObj: ICrossAccountId, label='unnest token'): Promise<boolean> {1358 const rootTokenAddress = {Ethereum: this.helper.util.getNestingTokenAddress(rootTokenObj.collectionId, rootTokenObj.tokenId)};1359 const result = await this.transferTokenFrom(signer, tokenObj.collectionId, tokenObj.tokenId, rootTokenAddress, toAddressObj);1360 if(!result) {1361 throw Error(`Unable to unnest token for ${label}`);1362 }1363 return result;1364 }13651366 1367136813691370137113721373137413751376137713781379 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, label = 'new collection'): Promise<UniqueNFTCollection> {1380 return await super.mintCollection(signer, collectionOptions, 'NFT', `Unable to mint NFT collection for ${label}`) as UniqueNFTCollection;1381 }13821383 1384138513861387138813891390 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; properties?: IProperty[]; }, label?: string): Promise<UniqueNFTToken> {1391 if(typeof label === 'undefined') label = `collection #${data.collectionId}`;1392 const creationResult = await this.helper.executeExtrinsic(1393 signer,1394 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1395 nft: {1396 properties: data.properties,1397 },1398 }],1399 true, `Unable to mint NFT token for ${label}`,1400 );1401 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult, label);1402 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1403 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1404 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1405 }14061407 1408140914101411141214131414141514161417141814191420142114221423 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[], label?: string): Promise<UniqueNFTToken[]> {1424 if(typeof label === 'undefined') label = `collection #${collectionId}`;1425 const creationResult = await this.helper.executeExtrinsic(1426 signer,1427 'api.tx.unique.createMultipleItemsEx', [collectionId, {NFT: tokens}],1428 true, `Unable to mint NFT tokens for ${label}`,1429 );1430 const collection = this.getCollectionObject(collectionId);1431 return this.helper.util.extractTokensFromCreationResult(creationResult, label).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1432 }14331434 1435143614371438143914401441144214431444144514461447144814491450145114521453 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {properties?: IProperty[]}[], label?: string): Promise<UniqueNFTToken[]> {1454 if(typeof label === 'undefined') label = `collection #${collectionId}`;1455 const rawTokens = [];1456 for (const token of tokens) {1457 const raw = {NFT: {properties: token.properties}};1458 rawTokens.push(raw);1459 }1460 const creationResult = await this.helper.executeExtrinsic(1461 signer,1462 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1463 true, `Unable to mint NFT tokens for ${label}`,1464 );1465 const collection = this.getCollectionObject(collectionId);1466 return this.helper.util.extractTokensFromCreationResult(creationResult, label).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1467 }14681469 147014711472147314741475147614771478 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, label?: string): Promise<{ success: boolean; token: number | null; }> {1479 return await super.burnToken(signer, collectionId, tokenId, label, 1n);1480 }14811482 14831484148514861487148814891490149114921493 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, label?: string) {1494 return super.approveToken(signer, collectionId, tokenId, toAddressObj, label, 1n);1495 }1496}149714981499class RFTGroup extends NFTnRFT {1500 150115021503150415051506 getCollectionObject(collectionId: number): UniqueRFTCollection {1507 return new UniqueRFTCollection(collectionId, this.helper);1508 }15091510 1511151215131514151515161517 getTokenObject(collectionId: number, tokenId: number): UniqueRFTToken {1518 return new UniqueRFTToken(tokenId, this.getCollectionObject(collectionId));1519 }15201521 1522152315241525152615271528 async getTokenTop10Owners(collectionId: number, tokenId: number): Promise<ICrossAccountId[]> {1529 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, tokenId])).toJSON().map(crossAccountIdFromLower);1530 }15311532 15331534153515361537153815391540 async getTokenBalance(collectionId: number, tokenId: number, addressObj: ICrossAccountId): Promise<bigint> {1541 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, tokenId])).toBigInt();1542 }15431544 1545154615471548154915501551155215531554 async transferToken(signer: TSigner, collectionId: number, tokenId: number, addressObj: ICrossAccountId, amount=100n): Promise<boolean> {1555 return await super.transferToken(signer, collectionId, tokenId, addressObj, amount);1556 }15571558 15591560156115621563156415651566156715681569 async transferTokenFrom(signer: TSigner, collectionId: number, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=100n): Promise<boolean> {1570 return await super.transferTokenFrom(signer, collectionId, tokenId, fromAddressObj, toAddressObj, amount);1571 }15721573 1574157515761577157815791580158115821583158415851586 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, label = 'new collection'): Promise<UniqueRFTCollection> {1587 return await super.mintCollection(signer, collectionOptions, 'RFT', `Unable to mint RFT collection for ${label}`) as UniqueRFTCollection;1588 }15891590 15911592159315941595159615971598 async mintToken(signer: TSigner, data: { collectionId: number; owner: ICrossAccountId | string; pieces: bigint; properties?: IProperty[]; }, label?: string): Promise<UniqueRFTToken> {1599 if(typeof label === 'undefined') label = `collection #${data.collectionId}`;1600 const creationResult = await this.helper.executeExtrinsic(1601 signer,1602 'api.tx.unique.createItem', [data.collectionId, (typeof data.owner === 'string') ? {Substrate: data.owner} : data.owner, {1603 refungible: {1604 pieces: data.pieces,1605 properties: data.properties,1606 },1607 }],1608 true, `Unable to mint RFT token for ${label}`,1609 );1610 const createdTokens = this.helper.util.extractTokensFromCreationResult(creationResult, label);1611 if (createdTokens.tokens.length > 1) throw Error('Minted multiple tokens');1612 if (createdTokens.tokens.length < 1) throw Error('No tokens minted');1613 return this.getTokenObject(data.collectionId, createdTokens.tokens[0].tokenId);1614 }16151616 async mintMultipleTokens(signer: TSigner, collectionId: number, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[], label?: string): Promise<UniqueRFTToken[]> {1617 throw Error('Not implemented');1618 if(typeof label === 'undefined') label = `collection #${collectionId}`;1619 const creationResult = await this.helper.executeExtrinsic(1620 signer,1621 'api.tx.unique.createMultipleItemsEx', [collectionId, {RefungibleMultipleOwners: tokens}],1622 true, `Unable to mint RFT tokens for ${label}`,1623 );1624 const collection = this.getCollectionObject(collectionId);1625 return this.helper.util.extractTokensFromCreationResult(creationResult, label).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1626 }16271628 1629163016311632163316341635163616371638 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {pieces: bigint, properties?: IProperty[]}[], label?: string): Promise<UniqueRFTToken[]> {1639 if(typeof label === 'undefined') label = `collection #${collectionId}`;1640 const rawTokens = [];1641 for (const token of tokens) {1642 const raw = {ReFungible: {pieces: token.pieces, properties: token.properties}};1643 rawTokens.push(raw);1644 }1645 const creationResult = await this.helper.executeExtrinsic(1646 signer,1647 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1648 true, `Unable to mint RFT tokens for ${label}`,1649 );1650 const collection = this.getCollectionObject(collectionId);1651 return this.helper.util.extractTokensFromCreationResult(creationResult, label).tokens.map((x: IToken) => collection.getTokenObject(x.tokenId));1652 }16531654 1655165616571658165916601661166216631664 async burnToken(signer: IKeyringPair, collectionId: number, tokenId: number, label?: string, amount=100n): Promise<{ success: boolean; token: number | null; }> {1665 return await super.burnToken(signer, collectionId, tokenId, label, amount);1666 }16671668 166916701671167216731674167516761677167816791680 async approveToken(signer: IKeyringPair, collectionId: number, tokenId: number, toAddressObj: ICrossAccountId, label?: string, amount=100n) {1681 return super.approveToken(signer, collectionId, tokenId, toAddressObj, label, amount);1682 }16831684 1685168616871688168916901691 async getTokenTotalPieces(collectionId: number, tokenId: number): Promise<bigint> {1692 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, tokenId])).unwrap().toBigInt();1693 }16941695 1696169716981699170017011702170317041705 async repartitionToken(signer: TSigner, collectionId: number, tokenId: number, amount: bigint, label?: string): Promise<boolean> {1706 if(typeof label === 'undefined') label = `collection #${collectionId}`;1707 const currentAmount = await this.getTokenTotalPieces(collectionId, tokenId);1708 const repartitionResult = await this.helper.executeExtrinsic(1709 signer,1710 'api.tx.unique.repartition', [collectionId, tokenId, amount],1711 true, `Unable to repartition RFT token for ${label}`,1712 );1713 if(currentAmount < amount) return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemCreated', label);1714 return this.helper.util.findCollectionInEvents(repartitionResult.result.events, collectionId, 'common', 'ItemDestroyed', label);1715 }1716}171717181719class FTGroup extends CollectionGroup {1720 172117221723172417251726 getCollectionObject(collectionId: number): UniqueFTCollection {1727 return new UniqueFTCollection(collectionId, this.helper);1728 }17291730 17311732173317341735173617371738173917401741174217431744 async mintCollection(signer: TSigner, collectionOptions: ICollectionCreationOptions, decimalPoints = 0, errorLabel = 'Unable to mint collection'): Promise<UniqueFTCollection> {1745 collectionOptions = JSON.parse(JSON.stringify(collectionOptions)) as ICollectionCreationOptions; 1746 if(collectionOptions.tokenPropertyPermissions) throw Error('Fungible collections has no tokenPropertyPermissions');1747 collectionOptions.mode = {fungible: decimalPoints};1748 for (const key of ['name', 'description', 'tokenPrefix']) {1749 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);1750 }1751 const creationResult = await this.helper.executeExtrinsic(1752 signer,1753 'api.tx.unique.createCollectionEx', [collectionOptions],1754 true, errorLabel,1755 );1756 return this.getCollectionObject(this.helper.util.extractCollectionIdFromCreationResult(creationResult, errorLabel));1757 }17581759 1760176117621763176417651766176717681769 async mintTokens(signer: TSigner, collectionId: number, owner: ICrossAccountId | string, amount: bigint, label?: string): Promise<boolean> {1770 if(typeof label === 'undefined') label = `collection #${collectionId}`;1771 const creationResult = await this.helper.executeExtrinsic(1772 signer,1773 'api.tx.unique.createItem', [collectionId, (typeof owner === 'string') ? {Substrate: owner} : owner, {1774 fungible: {1775 value: amount,1776 },1777 }],1778 true, `Unable to mint fungible tokens for ${label}`,1779 );1780 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated', label);1781 }17821783 178417851786178717881789179017911792 async mintMultipleTokensWithOneOwner(signer: TSigner, collectionId: number, owner: ICrossAccountId, tokens: {value: bigint}[], label?: string): Promise<boolean> {1793 if(typeof label === 'undefined') label = `collection #${collectionId}`;1794 const rawTokens = [];1795 for (const token of tokens) {1796 const raw = {Fungible: {Value: token.value}};1797 rawTokens.push(raw);1798 }1799 const creationResult = await this.helper.executeExtrinsic(1800 signer,1801 'api.tx.unique.createMultipleItems', [collectionId, owner, rawTokens],1802 true, `Unable to mint RFT tokens for ${label}`,1803 );1804 return this.helper.util.findCollectionInEvents(creationResult.result.events, collectionId, 'common', 'ItemCreated', label);1805 }18061807 180818091810181118121813 async getTop10Owners(collectionId: number): Promise<ICrossAccountId[]> {1814 return (await this.helper.callRpc('api.rpc.unique.tokenOwners', [collectionId, 0])).toJSON().map(crossAccountIdFromLower);1815 }18161817 1818181918201821182218231824 async getBalance(collectionId: number, addressObj: ICrossAccountId): Promise<bigint> {1825 return (await this.helper.callRpc('api.rpc.unique.balance', [collectionId, addressObj, 0])).toBigInt();1826 }18271828 182918301831183218331834183518361837 async transfer(signer: TSigner, collectionId: number, toAddressObj: ICrossAccountId, amount: bigint) {1838 return await super.transferToken(signer, collectionId, 0, toAddressObj, amount);1839 }18401841 1842184318441845184618471848184918501851 async transferFrom(signer: TSigner, collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount: bigint) {1852 return await super.transferTokenFrom(signer, collectionId, 0, fromAddressObj, toAddressObj, amount);1853 }18541855 185618571858185918601861186218631864 async burnTokens(signer: IKeyringPair, collectionId: number, amount=100n, label?: string): Promise<boolean> {1865 return (await super.burnToken(signer, collectionId, 0, label, amount)).success;1866 }18671868 1869187018711872187318741875187618771878 async burnTokensFrom(signer: IKeyringPair, collectionId: number, fromAddressObj: ICrossAccountId, amount=100n, label?: string): Promise<boolean> {1879 return await super.burnTokenFrom(signer, collectionId, fromAddressObj, 0, label, amount);1880 }18811882 18831884188518861887 async getTotalPieces(collectionId: number): Promise<bigint> {1888 return (await this.helper.callRpc('api.rpc.unique.totalPieces', [collectionId, 0])).unwrap().toBigInt();1889 }18901891 18921893189418951896189718981899190019011902 async approveTokens(signer: IKeyringPair, collectionId: number, toAddressObj: ICrossAccountId, amount=100n, label?: string) {1903 return super.approveToken(signer, collectionId, 0, toAddressObj, label, amount);1904 }19051906 1907190819091910191119121913 async getApprovedTokens(collectionId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {1914 return super.getTokenApprovedPieces(collectionId, 0, toAddressObj, fromAddressObj);1915 }1916}191719181919class ChainGroup extends HelperGroup {1920 19211922192319241925 getChainProperties(): IChainProperties {1926 const properties = (this.helper.api as any).registry.getChainProperties().toJSON();1927 return {1928 ss58Format: properties.ss58Format.toJSON(),1929 tokenDecimals: properties.tokenDecimals.toJSON(),1930 tokenSymbol: properties.tokenSymbol.toJSON(),1931 };1932 }19331934 19351936193719381939 async getLatestBlockNumber(): Promise<number> {1940 return (await this.helper.callRpc('api.rpc.chain.getHeader')).number.toNumber();1941 }19421943 194419451946194719481949 async getBlockHashByNumber(blockNumber: number): Promise<string | null> {1950 const blockHash = (await this.helper.callRpc('api.rpc.chain.getBlockHash', [blockNumber])).toJSON();1951 if(blockHash === '0x0000000000000000000000000000000000000000000000000000000000000000') return null;1952 return blockHash;1953 }19541955 195619571958195919601961 async getNonce(address: TSubstrateAccount): Promise<number> {1962 return (await (this.helper.api as any).query.system.account(address)).nonce.toNumber();1963 }1964}196519661967class BalanceGroup extends HelperGroup {1968 19691970197119721973 getOneTokenNominal(): bigint {1974 const chainProperties = this.helper.chain.getChainProperties();1975 return 10n ** BigInt((chainProperties.tokenDecimals || [18])[0]);1976 }19771978 197919801981198219831984 async getSubstrate(address: TSubstrateAccount): Promise<bigint> {1985 return (await this.helper.callRpc('api.query.system.account', [address])).data.free.toBigInt();1986 }19871988 198919901991199219931994 async getEthereum(address: TEthereumAccount): Promise<bigint> {1995 return (await this.helper.callRpc('api.rpc.eth.getBalance', [address])).toBigInt();1996 }19971998 19992000200120022003200420052006 async transferToSubstrate(signer: TSigner, address: TSubstrateAccount, amount: bigint | string): Promise<boolean> {2007 const result = await this.helper.executeExtrinsic(signer, 'api.tx.balances.transfer', [address, amount], true, `Unable to transfer balance from ${this.helper.getSignerAddress(signer)} to ${address}`);20082009 let transfer = {from: null, to: null, amount: 0n} as any;2010 result.result.events.forEach(({event: {data, method, section}}) => {2011 if ((section === 'balances') && (method === 'Transfer')) {2012 transfer = {2013 from: this.helper.address.normalizeSubstrate(data[0]),2014 to: this.helper.address.normalizeSubstrate(data[1]),2015 amount: BigInt(data[2]),2016 };2017 }2018 });2019 let isSuccess = this.helper.address.normalizeSubstrate(typeof signer === 'string' ? signer : signer.address) === transfer.from;2020 isSuccess = isSuccess && this.helper.address.normalizeSubstrate(address) === transfer.to;2021 isSuccess = isSuccess && BigInt(amount) === transfer.amount;2022 return isSuccess;2023 }2024}202520262027class AddressGroup extends HelperGroup {2028 2029203020312032203320342035 normalizeSubstrate(address: TSubstrateAccount, ss58Format = 42): TSubstrateAccount {2036 return this.helper.util.normalizeSubstrateAddress(address, ss58Format);2037 }20382039 204020412042204320442045 async normalizeSubstrateToChainFormat(address: TSubstrateAccount): Promise<TSubstrateAccount> {2046 const info = this.helper.chain.getChainProperties();2047 return encodeAddress(decodeAddress(address), info.ss58Format);2048 }20492050 2051205220532054205520562057 async ethToSubstrate(ethAddress: TEthereumAccount, toChainFormat=false): Promise<TSubstrateAccount> {2058 if(!toChainFormat) return evmToAddress(ethAddress);2059 const info = this.helper.chain.getChainProperties();2060 return evmToAddress(ethAddress, info.ss58Format);2061 }20622063 206420652066206720682069 substrateToEth(subAddress: TSubstrateAccount): TEthereumAccount {2070 return nesting.toChecksumAddress('0x' + Array.from(addressToEvm(subAddress), i => i.toString(16).padStart(2, '0')).join(''));2071 }2072}207320742075export class UniqueHelper extends ChainHelperBase {2076 chain: ChainGroup;2077 balance: BalanceGroup;2078 address: AddressGroup;2079 collection: CollectionGroup;2080 nft: NFTGroup;2081 rft: RFTGroup;2082 ft: FTGroup;20832084 constructor(logger?: ILogger) {2085 super(logger);2086 this.chain = new ChainGroup(this);2087 this.balance = new BalanceGroup(this);2088 this.address = new AddressGroup(this);2089 this.collection = new CollectionGroup(this);2090 this.nft = new NFTGroup(this);2091 this.rft = new RFTGroup(this);2092 this.ft = new FTGroup(this);2093 } 2094}209520962097class UniqueCollectionBase {2098 helper: UniqueHelper;2099 collectionId: number;21002101 constructor(collectionId: number, uniqueHelper: UniqueHelper) {2102 this.collectionId = collectionId;2103 this.helper = uniqueHelper;2104 }21052106 async getData() {2107 return await this.helper.collection.getData(this.collectionId);2108 }21092110 async getLastTokenId() {2111 return await this.helper.collection.getLastTokenId(this.collectionId);2112 }21132114 async isTokenExists(tokenId: number) {2115 return await this.helper.collection.isTokenExists(this.collectionId, tokenId);2116 }21172118 async getAdmins() {2119 return await this.helper.collection.getAdmins(this.collectionId);2120 }21212122 async getEffectiveLimits() {2123 return await this.helper.collection.getEffectiveLimits(this.collectionId);2124 }21252126 async setSponsor(signer: TSigner, sponsorAddress: TSubstrateAccount, label?: string) {2127 return await this.helper.collection.setSponsor(signer, this.collectionId, sponsorAddress, label);2128 }21292130 async confirmSponsorship(signer: TSigner, label?: string) {2131 return await this.helper.collection.confirmSponsorship(signer, this.collectionId, label);2132 }21332134 async setLimits(signer: TSigner, limits: ICollectionLimits, label?: string) {2135 return await this.helper.collection.setLimits(signer, this.collectionId, limits, label);2136 }21372138 async changeOwner(signer: TSigner, ownerAddress: TSubstrateAccount, label?: string) {2139 return await this.helper.collection.changeOwner(signer, this.collectionId, ownerAddress, label);2140 }21412142 async addAdmin(signer: TSigner, adminAddressObj: ICrossAccountId, label?: string) {2143 return await this.helper.collection.addAdmin(signer, this.collectionId, adminAddressObj, label);2144 }21452146 async removeAdmin(signer: TSigner, adminAddressObj: ICrossAccountId, label?: string) {2147 return await this.helper.collection.removeAdmin(signer, this.collectionId, adminAddressObj, label);2148 }21492150 async setProperties(signer: TSigner, properties: IProperty[], label?: string) {2151 return await this.helper.collection.setProperties(signer, this.collectionId, properties, label);2152 }21532154 async deleteProperties(signer: TSigner, propertyKeys: string[], label?: string) {2155 return await this.helper.collection.deleteProperties(signer, this.collectionId, propertyKeys, label);2156 }21572158 async getTokenNextSponsored(tokenId: number, addressObj: ICrossAccountId) {2159 return await this.helper.collection.getTokenNextSponsored(this.collectionId, tokenId, addressObj);2160 }21612162 async setPermissions(signer: TSigner, permissions: ICollectionPermissions, label?: string) {2163 return await this.helper.collection.setPermissions(signer, this.collectionId, permissions, label);2164 }21652166 async enableNesting(signer: TSigner, permissions: INestingPermissions, label?: string) {2167 return await this.helper.collection.enableNesting(signer, this.collectionId, permissions, label);2168 }21692170 async disableNesting(signer: TSigner, label?: string) {2171 return await this.helper.collection.disableNesting(signer, this.collectionId, label);2172 }21732174 async burn(signer: TSigner, label?: string) {2175 return await this.helper.collection.burn(signer, this.collectionId, label);2176 }2177}217821792180class UniqueNFTCollection extends UniqueCollectionBase {2181 getTokenObject(tokenId: number) {2182 return new UniqueNFTToken(tokenId, this);2183 }21842185 async getTokensByAddress(addressObj: ICrossAccountId) {2186 return await this.helper.nft.getTokensByAddress(this.collectionId, addressObj);2187 }21882189 async getToken(tokenId: number, blockHashAt?: string) {2190 return await this.helper.nft.getToken(this.collectionId, tokenId, blockHashAt);2191 }21922193 async getTokenOwner(tokenId: number, blockHashAt?: string) {2194 return await this.helper.nft.getTokenOwner(this.collectionId, tokenId, blockHashAt);2195 }21962197 async getTokenTopmostOwner(tokenId: number, blockHashAt?: string) {2198 return await this.helper.nft.getTokenTopmostOwner(this.collectionId, tokenId, blockHashAt);2199 }22002201 async getTokenChildren(tokenId: number, blockHashAt?: string) {2202 return await this.helper.nft.getTokenChildren(this.collectionId, tokenId, blockHashAt);2203 }22042205 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId) {2206 return await this.helper.nft.transferToken(signer, this.collectionId, tokenId, addressObj);2207 }22082209 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2210 return await this.helper.nft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj);2211 }22122213 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, label?: string) {2214 return await this.helper.nft.approveToken(signer, this.collectionId, tokenId, toAddressObj, label);2215 }22162217 async isTokenApproved(tokenId: number, toAddressObj: ICrossAccountId) {2218 return await this.helper.nft.isTokenApproved(this.collectionId, tokenId, toAddressObj);2219 }22202221 async mintToken(signer: TSigner, owner: ICrossAccountId, properties?: IProperty[], label?: string) {2222 return await this.helper.nft.mintToken(signer, {collectionId: this.collectionId, owner, properties}, label);2223 }22242225 async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, properties?: IProperty[]}[], label?: string) {2226 return await this.helper.nft.mintMultipleTokens(signer, this.collectionId, tokens, label);2227 }22282229 async burnToken(signer: TSigner, tokenId: number, label?: string) {2230 return await this.helper.nft.burnToken(signer, this.collectionId, tokenId, label);2231 }22322233 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[], label?: string) {2234 return await this.helper.nft.setTokenProperties(signer, this.collectionId, tokenId, properties, label);2235 }22362237 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[], label?: string) {2238 return await this.helper.nft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys, label);2239 }22402241 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[], label?: string) {2242 return await this.helper.nft.setTokenPropertyPermissions(signer, this.collectionId, permissions, label);2243 }22442245 async nestToken(signer: TSigner, tokenId: number, toTokenObj: IToken, label?: string) {2246 return await this.helper.nft.nestToken(signer, {collectionId: this.collectionId, tokenId}, toTokenObj, label);2247 }22482249 async unnestToken(signer: TSigner, tokenId: number, fromTokenObj: IToken, toAddressObj: ICrossAccountId, label?: string) {2250 return await this.helper.nft.unnestToken(signer, {collectionId: this.collectionId, tokenId}, fromTokenObj, toAddressObj, label);2251 }2252}225322542255class UniqueRFTCollection extends UniqueCollectionBase {2256 getTokenObject(tokenId: number) {2257 return new UniqueRFTToken(tokenId, this);2258 }22592260 async getTokensByAddress(addressObj: ICrossAccountId) {2261 return await this.helper.rft.getTokensByAddress(this.collectionId, addressObj);2262 }22632264 async getTop10TokenOwners(tokenId: number) {2265 return await this.helper.rft.getTokenTop10Owners(this.collectionId, tokenId);2266 }22672268 async getTokenBalance(tokenId: number, addressObj: ICrossAccountId) {2269 return await this.helper.rft.getTokenBalance(this.collectionId, tokenId, addressObj);2270 }22712272 async getTokenTotalPieces(tokenId: number) {2273 return await this.helper.rft.getTokenTotalPieces(this.collectionId, tokenId);2274 }22752276 async transferToken(signer: TSigner, tokenId: number, addressObj: ICrossAccountId, amount=100n) {2277 return await this.helper.rft.transferToken(signer, this.collectionId, tokenId, addressObj, amount);2278 }22792280 async transferTokenFrom(signer: TSigner, tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=100n) {2281 return await this.helper.rft.transferTokenFrom(signer, this.collectionId, tokenId, fromAddressObj, toAddressObj, amount);2282 }22832284 async approveToken(signer: TSigner, tokenId: number, toAddressObj: ICrossAccountId, amount=100n, label?: string) {2285 return await this.helper.rft.approveToken(signer, this.collectionId, tokenId, toAddressObj, label, amount);2286 }22872288 async getTokenApprovedPieces(tokenId: number, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2289 return await this.helper.rft.getTokenApprovedPieces(this.collectionId, tokenId, toAddressObj, fromAddressObj);2290 }22912292 async repartitionToken(signer: TSigner, tokenId: number, amount: bigint, label?: string) {2293 return await this.helper.rft.repartitionToken(signer, this.collectionId, tokenId, amount, label);2294 }22952296 async mintToken(signer: TSigner, owner: ICrossAccountId, pieces=100n, properties?: IProperty[], label?: string) {2297 return await this.helper.rft.mintToken(signer, {collectionId: this.collectionId, owner, pieces, properties}, label);2298 }22992300 async mintMultipleTokens(signer: TSigner, tokens: {owner: ICrossAccountId, pieces: bigint, properties?: IProperty[]}[], label?: string) {2301 return await this.helper.rft.mintMultipleTokens(signer, this.collectionId, tokens, label);2302 }23032304 async burnToken(signer: TSigner, tokenId: number, amount=100n, label?: string) {2305 return await this.helper.rft.burnToken(signer, this.collectionId, tokenId, label, amount);2306 }23072308 async setTokenProperties(signer: TSigner, tokenId: number, properties: IProperty[], label?: string) {2309 return await this.helper.rft.setTokenProperties(signer, this.collectionId, tokenId, properties, label);2310 }23112312 async deleteTokenProperties(signer: TSigner, tokenId: number, propertyKeys: string[], label?: string) {2313 return await this.helper.rft.deleteTokenProperties(signer, this.collectionId, tokenId, propertyKeys, label);2314 }23152316 async setTokenPropertyPermissions(signer: TSigner, permissions: ITokenPropertyPermission[], label?: string) {2317 return await this.helper.rft.setTokenPropertyPermissions(signer, this.collectionId, permissions, label);2318 }2319}232023212322class UniqueFTCollection extends UniqueCollectionBase {2323 async mint(signer: TSigner, owner: ICrossAccountId, amount: bigint, label?: string) {2324 return await this.helper.ft.mintTokens(signer, this.collectionId, owner, amount, label);2325 }23262327 async mintWithOneOwner(signer: TSigner, owner: ICrossAccountId, tokens: {value: bigint}[], label?: string) {2328 return await this.helper.ft.mintMultipleTokensWithOneOwner(signer, this.collectionId, owner, tokens, label);2329 }23302331 async getBalance(addressObj: ICrossAccountId) {2332 return await this.helper.ft.getBalance(this.collectionId, addressObj);2333 }23342335 async getTop10Owners() {2336 return await this.helper.ft.getTop10Owners(this.collectionId);2337 }23382339 async transfer(signer: TSigner, toAddressObj: ICrossAccountId, amount: bigint) {2340 return await this.helper.ft.transfer(signer, this.collectionId, toAddressObj, amount);2341 }23422343 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount: bigint) {2344 return await this.helper.ft.transferFrom(signer, this.collectionId, fromAddressObj, toAddressObj, amount);2345 }23462347 async burnTokens(signer: TSigner, amount: bigint, label?: string) {2348 return await this.helper.ft.burnTokens(signer, this.collectionId, amount, label);2349 }23502351 async burnTokensFrom(signer: TSigner, fromAddressObj: ICrossAccountId, amount: bigint, label?: string) {2352 return await this.helper.ft.burnTokensFrom(signer, this.collectionId, fromAddressObj, amount, label);2353 }23542355 async getTotalPieces() {2356 return await this.helper.ft.getTotalPieces(this.collectionId);2357 }23582359 async approveTokens(signer: TSigner, toAddressObj: ICrossAccountId, amount=100n, label?: string) {2360 return await this.helper.ft.approveTokens(signer, this.collectionId, toAddressObj, amount, label);2361 }23622363 async getApprovedTokens(fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2364 return await this.helper.ft.getApprovedTokens(this.collectionId, fromAddressObj, toAddressObj);2365 }2366}236723682369class UniqueTokenBase implements IToken {2370 collection: UniqueNFTCollection | UniqueRFTCollection;2371 collectionId: number;2372 tokenId: number;23732374 constructor(tokenId: number, collection: UniqueNFTCollection | UniqueRFTCollection) {2375 this.collection = collection;2376 this.collectionId = collection.collectionId;2377 this.tokenId = tokenId;2378 }23792380 async getNextSponsored(addressObj: ICrossAccountId) {2381 return await this.collection.getTokenNextSponsored(this.tokenId, addressObj);2382 }23832384 async setProperties(signer: TSigner, properties: IProperty[], label?: string) {2385 return await this.collection.setTokenProperties(signer, this.tokenId, properties, label);2386 }23872388 async deleteProperties(signer: TSigner, propertyKeys: string[], label?: string) {2389 return await this.collection.deleteTokenProperties(signer, this.tokenId, propertyKeys, label);2390 }2391}239223932394class UniqueNFTToken extends UniqueTokenBase {2395 collection: UniqueNFTCollection;23962397 constructor(tokenId: number, collection: UniqueNFTCollection) {2398 super(tokenId, collection);2399 this.collection = collection;2400 }24012402 async getData(blockHashAt?: string) {2403 return await this.collection.getToken(this.tokenId, blockHashAt);2404 }24052406 async getOwner(blockHashAt?: string) {2407 return await this.collection.getTokenOwner(this.tokenId, blockHashAt);2408 }24092410 async getTopmostOwner(blockHashAt?: string) {2411 return await this.collection.getTokenTopmostOwner(this.tokenId, blockHashAt);2412 }24132414 async getChildren(blockHashAt?: string) {2415 return await this.collection.getTokenChildren(this.tokenId, blockHashAt);2416 }24172418 async nest(signer: TSigner, toTokenObj: IToken, label?: string) {2419 return await this.collection.nestToken(signer, this.tokenId, toTokenObj, label);2420 }24212422 async unnest(signer: TSigner, fromTokenObj: IToken, toAddressObj: ICrossAccountId, label?: string) {2423 return await this.collection.unnestToken(signer, this.tokenId, fromTokenObj, toAddressObj, label);2424 }24252426 async transfer(signer: TSigner, addressObj: ICrossAccountId) {2427 return await this.collection.transferToken(signer, this.tokenId, addressObj);2428 }24292430 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId) {2431 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj);2432 }24332434 async approve(signer: TSigner, toAddressObj: ICrossAccountId, label?: string) {2435 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, label);2436 }24372438 async isApproved(toAddressObj: ICrossAccountId) {2439 return await this.collection.isTokenApproved(this.tokenId, toAddressObj);2440 }24412442 async burn(signer: TSigner, label?: string) {2443 return await this.collection.burnToken(signer, this.tokenId, label);2444 }2445}24462447class UniqueRFTToken extends UniqueTokenBase {2448 collection: UniqueRFTCollection;24492450 constructor(tokenId: number, collection: UniqueRFTCollection) {2451 super(tokenId, collection);2452 this.collection = collection;2453 }24542455 async getTop10Owners() {2456 return await this.collection.getTop10TokenOwners(this.tokenId);2457 }24582459 async getBalance(addressObj: ICrossAccountId) {2460 return await this.collection.getTokenBalance(this.tokenId, addressObj);2461 }24622463 async getTotalPieces() {2464 return await this.collection.getTokenTotalPieces(this.tokenId);2465 }24662467 async transfer(signer: TSigner, addressObj: ICrossAccountId, amount=100n) {2468 return await this.collection.transferToken(signer, this.tokenId, addressObj, amount);2469 }24702471 async transferFrom(signer: TSigner, fromAddressObj: ICrossAccountId, toAddressObj: ICrossAccountId, amount=100n) {2472 return await this.collection.transferTokenFrom(signer, this.tokenId, fromAddressObj, toAddressObj, amount);2473 }24742475 async approve(signer: TSigner, toAddressObj: ICrossAccountId, amount=100n, label?: string) {2476 return await this.collection.approveToken(signer, this.tokenId, toAddressObj, amount, label);2477 }24782479 async getApprovedPieces(fromAddressObj: ICrossAccountId, toAccountObj: ICrossAccountId) {2480 return await this.collection.getTokenApprovedPieces(this.tokenId, fromAddressObj, toAccountObj);2481 }24822483 async repartition(signer: TSigner, amount: bigint, label?: string) {2484 return await this.collection.repartitionToken(signer, this.tokenId, amount, label);2485 }24862487 async burn(signer: TSigner, amount=100n, label?: string) {2488 return await this.collection.burnToken(signer, this.tokenId, amount, label);2489 }2490}