@broxus/js-core
Version:
MobX-based JavaScript Core library
317 lines (316 loc) • 13.1 kB
JavaScript
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, debug, sliceString, throwException } from '@broxus/js-utils';
import { action, computed, makeObservable } from 'mobx';
import { inheritTextStyle, successLabelStyle, successTextStyle, warningLabelStyle, warningTextStyle } from '../../console';
import { SmartContractModel } from '../../core';
import { DexPoolType } from '../../models/dex';
import { DexPair } from '../../models/dex-pair';
import { DexPoolUtils } from '../../models/dex-pool/DexPoolUtils';
import { DexStablePair } from '../../models/dex-stable-pair';
import { DexStablePool } from '../../models/dex-stable-pool';
import { areAddressesEqual, contractStateChangeDebugMessage, subscribeDebugMessage, syncErrorMessage, unsubscribeDebugMessage, unsubscribeErrorMessage, } from '../../utils';
export class DexPool extends SmartContractModel {
_connection;
dex;
options;
_provider;
static Utils = DexPoolUtils;
/**
* @param {ProviderRpcClient} _connection
* Standalone RPC client that doesn't require connection to the TVM wallet provider
* @param {Address | string} address
* DexPool root address
* @param {Dex} dex
* Dex Smart Contract Model instance
* @param {Readonly<DexPoolCtorOptions>} [options]
* (optional) DexPool Smart Contract Model options
* @param {ProviderRpcClient} [_provider]
* (optional) RPC provider that require connection to the TVM wallet
*/
constructor(_connection, address, dex, options, _provider) {
super(_connection, address);
this._connection = _connection;
this.dex = dex;
this.options = options;
this._provider = _provider;
this.setData(() => ({
feeParams: {},
}));
makeObservable(this);
}
/**
* @param {ProviderRpcClient} connection
* Standalone RPC client that doesn't require connection to the TVM wallet provider
* @param {Readonly<DexPoolCreateConfig>} config
* DexPool Smart Contract Model config
* @param {Readonly<DexPoolCreateOptions>} [options]
* (optional) DexPool Smart Contract Model options
* @param {ProviderRpcClient} [provider]
* (optional) RPC provider that require connection to the TVM wallet
*/
static async create(connection, config, options, provider) {
const { sync = true, watch, watchCallback, ...restOptions } = { ...options };
let address;
if ('address' in config) {
address = config.address;
}
else {
address = await config.dex.getExpectedPoolAddress({
roots: config.roots,
});
}
const dexSPool = new DexPool(connection, address, config.dex, restOptions, provider);
if (sync) {
await dexSPool.sync({ force: false });
}
if (watch) {
await dexSPool.watch(watchCallback);
}
return dexSPool;
}
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 });
if (!this.isDeployed) {
throwException('DexPool is not deployed');
}
const [details, feeParams, dexRootAddress, dexVaultAddress, type, virtualPrice] = await Promise.all([
DexPool.Utils.getDetails(this._connection, this.address, state),
DexPool.Utils.getFeeParams(this._connection, this.address, state),
DexPool.Utils.getRoot(this._connection, this.address, state),
DexPool.Utils.getVault(this._connection, this.address, state),
DexPool.Utils.getPoolType(this._connection, this.address, state),
DexPool.Utils.getVirtualPrice(this._connection, this.address, state),
]);
this.setData({ ...details, dexRootAddress, dexVaultAddress, feeParams, type, virtualPrice });
}
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 check(options) {
if (options?.force || !this.contractState) {
await this.syncContractState({ force: options?.force ?? true });
}
if (!this?.isDeployed) {
debug(`%c${this.constructor.name}%c Check for %c${sliceString(this.address?.toString())}%c =>%c not deployed`, warningLabelStyle, inheritTextStyle, successTextStyle, inheritTextStyle, warningTextStyle);
return false;
}
const isActive = await DexPool.Utils.isActive(this._connection, this.address, this.contractState);
if (!isActive) {
debug(`%c${this.constructor.name}%c Check for %c${sliceString(this.address?.toString())}%c =>%c inactive`, warningLabelStyle, inheritTextStyle, successTextStyle, inheritTextStyle, warningTextStyle);
return false;
}
debug(`%c${this.constructor.name}%c Check for %c${sliceString(this.address?.toString())}%c =>%c deployed`, successLabelStyle, inheritTextStyle, successTextStyle, inheritTextStyle, successTextStyle);
return true;
}
async syncBalances(options) {
if (!this.lpToken || !this.tokens.length) {
await this.sync({ force: true });
return DexPool.Utils.getBalances(this._connection, this.address, this.contractState);
}
await this.syncContractState({ force: options?.force ?? true });
const balances = await DexPool.Utils.getBalances(this._connection, this.address, this.contractState);
this.setData('balances', balances);
return balances;
}
async withdrawLiquidity(params, args) {
if (this.type === DexPoolType.CONSTANT_PRODUCT) {
const dexPair = await DexPair.create(this._connection, { address: this.address, dex: this.dex }, undefined, this._provider);
return dexPair.withdrawLiquidity(params, args);
}
if (this.type === DexPoolType.STABLE_PAIR) {
const dexStablePair = await DexStablePair.create(this._connection, { address: this.address, dex: this.dex }, undefined, this._provider);
return dexStablePair.withdrawLiquidity(params, args);
}
if (this.type === DexPoolType.STABLE_POOL) {
const dexStablePool = await DexStablePool.create(this._connection, { address: this.address, dex: this.dex }, undefined, this._provider);
return dexStablePool.withdrawLiquidity(params, args);
}
return undefined;
}
get balances() {
return this._data.balances;
}
get dexRootAddress() {
return this._data.dexRootAddress;
}
get dexVaultAddress() {
return this._data.dexVaultAddress;
}
get feeParams() {
return this._data.feeParams;
}
get isActive() {
return this._data.isActive;
}
get lpToken() {
return this._data.lpToken;
}
get lpWallet() {
return this._data.lpWallet;
}
get tokens() {
return this._data.tokens;
}
get type() {
return this._data.type;
}
get virtualPrice() {
return this._data.virtualPrice;
}
get wallets() {
return this._data.wallets;
}
}
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], DexPool.prototype, "sync", null);
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Function]),
__metadata("design:returntype", Promise)
], DexPool.prototype, "watch", null);
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], DexPool.prototype, "unwatch", null);
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], DexPool.prototype, "check", null);
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], DexPool.prototype, "syncBalances", null);
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], DexPool.prototype, "withdrawLiquidity", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "balances", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "dexRootAddress", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "dexVaultAddress", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "feeParams", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "isActive", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "lpToken", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "lpWallet", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "tokens", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "type", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "virtualPrice", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], DexPool.prototype, "wallets", null);