UNPKG

@broxus/js-core

Version:

MobX-based JavaScript Core library

373 lines (372 loc) 14.2 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { debounce, throwException } from '@broxus/js-utils'; import { Address } from 'everscale-inpage-provider'; import { action, computed, makeObservable } from 'mobx'; import { SECONDS_IN_HOUR } from '../../constants'; import { SmartContractModel } from '../../core'; import { TvmTokenWallet } from '../../models/tvm-token-wallet'; import { TvmTokenUtils } from '../../models/tvm-token/TvmTokenUtils'; import { areAddressesEqual, contractStateChangeDebugMessage, resolveTvmAddress, subscribeDebugMessage, syncErrorMessage, unsubscribeDebugMessage, unsubscribeErrorMessage, } from '../../utils'; export class TvmToken extends SmartContractModel { _connection; options; _provider; static Utils = TvmTokenUtils; static Wallet = TvmTokenWallet; /** * @template T * @param {ProviderRpcClient} _connection * Standalone RPC client that doesn't require connection to the TVM wallet provider * @param {TvmTokenCtorData['address'] | Readonly<T & TvmTokenCtorData>} data * TvmToken root address or an object of a token attributes (e.g. decimals, symbol, name) * @param {Readonly<GaugeAccountCtorOptions>} [options] * (optional) TvmToken Smart Contract Model options * @param {ProviderRpcClient} [_provider] * (optional) RPC provider that require connection to the TVM wallet */ constructor(_connection, data, options, _provider) { const isAddress = typeof data === 'string' || data instanceof Address; if (!isAddress && !('address' in data)) { throw new Error('Address is not specified'); } const address = resolveTvmAddress(isAddress ? data : data.address); super(_connection, address); this._connection = _connection; this.options = options; this._provider = _provider; if (isAddress) { this.setData(() => ({ address, tags: new Set(), })); } else if (data != null && typeof data === 'object' && !Array.isArray(data)) { this.setData(() => ({ ...data, address, tags: new Set(data.tags), })); } makeObservable(this); } /** * @template {TvmTokenCtorData | ObjectLiteral} V * @param {ProviderRpcClient} connection * Standalone RPC client that doesn't require connection to the TVM wallet provider * @param {TvmTokenCtorData['address'] | Readonly<V & TvmTokenCtorData>} data * TvmToken root address or an object of a token attributes (e.g. decimals, symbol, name) * @param {Readonly<TvmTokenCreateOptions>} [options] * (optional) TvmToken Smart Contract Model options * @param {ProviderRpcClient} [provider] * (optional) RPC provider that require connection to the TVM wallet */ static async create(connection, data, options, provider) { const { sync = true, watch, watchCallback, ...restOptions } = { ...options }; const token = new TvmToken(connection, data, restOptions, provider); if (sync) { await token.sync({ force: false, ttl: options?.ttl }); } if (watch) { await token.watch(watchCallback); } return token; } async sync(options) { if (!options?.force && this.isSyncing) { return; } try { this.setState('isSyncing', !options?.silent); const state = await this.syncContractState({ force: options?.force || !this.contractState, ttl: options?.ttl || SECONDS_IN_HOUR * 1000, }); await this.syncComputedStorageData(); if (!this.isDeployed) { throwException('Token is not deployed'); } const [details, burnByRootDisabled, burnPaused, mintDisabled] = await Promise.all([ TvmToken.Utils.getDetails(this._connection, this.address, state), TvmToken.Utils.burnByRootDisabled(this._connection, this.address, state), TvmToken.Utils.burnPaused(this._connection, this.address, state), TvmToken.Utils.mintDisabled(this._connection, this.address, state), ]); if (!details?.symbol || details.decimals == null) { throwException('Token has invalid data and cannot be resolved'); } this.setData({ burnByRootDisabled, burnPaused, mintDisabled, ...details }); } catch (e) { if (process.env.NODE_ENV !== 'production') { const state = await this._connection.getProviderState(); syncErrorMessage(this.constructor.name, this.address, e, state.networkId.toString()); } } finally { this.setState('isSyncing', false); } } async watch(callback) { try { this.contractSubscriber = new this._connection.Subscriber(); await this.contractSubscriber.states(this.address).delayed(stream => { if (process.env.NODE_ENV !== 'production') { subscribeDebugMessage(this.constructor.name, this.address); } return stream.on(debounce(async (event) => { if (process.env.NODE_ENV !== 'production') { const state = await this._connection.getProviderState(); contractStateChangeDebugMessage(this.constructor.name, this.address, event, state.networkId.toString()); } if (areAddressesEqual(event.address, this.address)) { await this.sync({ force: !this.isSyncing, silent: true }); callback?.(...this.toJSON(true)); return; } await this.unwatch(); }, this.options?.watchDebounceDelay ?? 3000)); }); return this.contractSubscriber; } catch (e) { await this.unwatch(); throw e; } } async unwatch() { try { await this.contractSubscriber?.unsubscribe(); this.contractSubscriber = undefined; if (process.env.NODE_ENV !== 'production') { unsubscribeDebugMessage(this.constructor.name, this.address); } } catch (e) { if (process.env.NODE_ENV !== 'production') { const state = await this._connection.getProviderState(); unsubscribeErrorMessage(this.constructor.name, this.address, e, state.networkId.toString()); } } } async wallet(ownerAddress, options) { const address = await this.walletOf(ownerAddress); return TvmToken.Wallet.create(this._connection, { address }, options, this._provider); } async walletOf(ownerAddress) { return TvmToken.Utils.walletOf(this._connection, { ownerAddress, tokenAddress: this.address, }, this.contractState); } async balance(ownerAddress) { const walletAddress = await this.walletOf(ownerAddress); return TvmToken.Wallet.Utils.balance(this._connection, { walletAddress }); } /** * Returns token `address` as a string. * @returns {string} */ get root() { return this.address.toString(); } /** * Returns token `chainId`. Basically describes in the assets manifest. * @returns {TvmTokenData['chainId']} */ get chainId() { return this._data.chainId; } /** * Returns token `decimals`. Can be described in the assets manifest or * retrieved from a blockchain by a token address * (use **TvmToken.Utils.getDecimals(address)**). * @returns {TvmTokenData['decimals']} */ get decimals() { return this._data.decimals; } /** * Returns token `logoURI`. Basically describes in the assets manifest. * @returns {TvmTokenData['logoURI']} */ get icon() { return this._data.logoURI; } /** * Returns token `name`. Can be described in the assets manifest or * retrieved from a blockchain by a token address * (use **TvmToken.Utils.getName(address)**) * @returns {TvmTokenData['name']} */ get name() { return this._data.name; } /** * Returns token `symbol`. Can be described in the assets manifest or * retrieved from a blockchain by a token address * (use **TvmToken.Utils.getSymbol(address)**) * @returns {TvmTokenData['symbol']} */ get symbol() { return this._data.symbol; } /** * Returns token `rootOwnerAddress`. Can be described in the assets manifest or * retrieved from a blockchain by a token address * (use **TvmToken.Utils.rootOwnerAddress(address)**) * @returns {TvmTokenData['rootOwnerAddress']} */ get rootOwnerAddress() { return this._data.rootOwnerAddress; } /** * Returns token `totalSupply`. Can be described in the assets manifest or * retrieved from a blockchain by a token address * (use **TvmToken.Utils.totalSupply(address)**) * @returns {TvmTokenData['totalSupply']} */ get totalSupply() { return this._data.totalSupply; } /** * Returns token `vendor` as organization slug. Basically describes in the assets manifest. * @returns {TvmTokenData['vendor']} */ get vendor() { return this._data.vendor; } /** * Returns token `verified` flag. Basically describes in the assets manifest. * @returns {TvmTokenData['verified']} */ get verified() { return this._data.verified; } get burnByRootDisabled() { return this._data.burnByRootDisabled; } get burnPaused() { return this._data.burnPaused; } get mintDisabled() { return this._data.mintDisabled; } get(key) { return this._data[key]; } /** * Returns copy of the current token */ clone() { return new TvmToken(this._connection, this.toJSON(), this.options, this._provider); } } __decorate([ action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], TvmToken.prototype, "sync", null); __decorate([ action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Function]), __metadata("design:returntype", Promise) ], TvmToken.prototype, "watch", null); __decorate([ action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], TvmToken.prototype, "unwatch", null); __decorate([ action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object, Object]), __metadata("design:returntype", Promise) ], TvmToken.prototype, "wallet", null); __decorate([ action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], TvmToken.prototype, "walletOf", null); __decorate([ action.bound, __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], TvmToken.prototype, "balance", null); __decorate([ computed, __metadata("design:type", String), __metadata("design:paramtypes", []) ], TvmToken.prototype, "root", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "chainId", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "decimals", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "icon", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "name", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "symbol", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "rootOwnerAddress", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "totalSupply", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "vendor", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "verified", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "burnByRootDisabled", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "burnPaused", null); __decorate([ computed, __metadata("design:type", Object), __metadata("design:paramtypes", []) ], TvmToken.prototype, "mintDisabled", null);