@broxus/js-core
Version:
MobX-based JavaScript Core library
429 lines (428 loc) • 16.3 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, throwException } from '@broxus/js-utils';
import { BigNumber } from 'bignumber.js';
import { action, computed, makeObservable } from 'mobx';
import { SECONDS_IN_HOUR } from '../../constants';
import { SmartContractModel } from '../../core';
import { CONFIG_ABI, CUR_VALIDATOR_CONFIG_ABI, FORWARD_FEE_PRICES_PARAM_ABI, PRICES_PARAM_ABI, VALIDATOR_CONFIG_ABI, } from '../../models/network-config/constants';
import { areAddressesEqual, contractStateChangeDebugMessage, subscribeDebugMessage, syncErrorMessage, unsubscribeDebugMessage, unsubscribeErrorMessage, } from '../../utils';
export class MasterChainConfig extends SmartContractModel {
_connection;
options;
_contract;
/**
* @param {ProviderRpcClient} _connection
* Standalone RPC client that doesn't require connection to the TVM wallet provider
* @param {Address | string} address
* MasterChainConfig root address
* @param {Readonly<MasterChainConfigCtorOptions>} [options]
* (optional) MasterChainConfig Smart Contract Model options
*/
constructor(_connection, address, options) {
super(_connection, address);
this._connection = _connection;
this.options = options;
this._contract = new this._connection.Contract(CONFIG_ABI, this.address);
makeObservable(this);
}
/**
* @param {ProviderRpcClient} connection
* Standalone RPC client that doesn't require connection to the TVM wallet provider
* @param {Address | string} address
* MasterChainConfig root address
* @param {Readonly<MasterChainCreateOptions>} [options]
* (optional) MasterChainConfig Smart Contract Model options
*/
static async create(connection, address, options) {
const { sync = true, watch, watchCallback, ...restOptions } = { ...options };
const config = new MasterChainConfig(connection, address, restOptions);
if (sync) {
await config.sync({ force: false, ttl: options?.ttl });
}
if (watch) {
await config.watch(watchCallback);
}
return config;
}
async sync(options) {
if (!options?.force && this.isSyncing) {
return;
}
try {
this.setState('isSyncing', !options?.silent);
await this.syncContractState({
force: options?.force || !this.contractState,
ttl: options?.ttl || SECONDS_IN_HOUR * 1000,
});
if (!this.isDeployed) {
throwException(`${this.constructor.name} is not deployed`);
}
await this.syncParams();
}
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() {
if (this.contractSubscriber === undefined) {
return;
}
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 syncParams() {
const { fields } = await this._contract.getFields({ allowPartial: true, cachedState: this.contractState });
if (fields == null) {
throw new Error('Config contract state not found');
}
const { boc: nonEmptyMap } = await this._connection.packIntoCell({
abiVersion: '2.2',
data: {
flag: true,
root: fields.paramsRoot,
},
structure: [
{ name: 'flag', type: 'bool' },
{ name: 'root', type: 'cell' },
],
});
const { data: { params: rawParams } } = await this._connection.unpackFromCell({
abiVersion: '2.2',
allowPartial: true,
boc: nonEmptyMap,
structure: [{ name: 'params', type: 'map(uint32,cell)' }],
});
const params = new Map();
for (const [id, value] of rawParams) {
params.set(parseInt(id, 10), value);
}
// Masterchain gas limits and prices.
// https://github.com/broxus/tycho-types/blob/master/src/models/config/mod.rs#L1040
const param20 = params.get(20);
// Message forwarding prices for masterchain.
// https://github.com/broxus/tycho-types/blob/master/src/models/config/mod.rs#L1064
const param24 = params.get(24);
// Masterchain gas limits and prices.
// https://github.com/broxus/tycho-types/blob/master/src/models/config/mod.rs#L1012
const param15 = params.get(15);
// Staking distribution values for base workchain
// https://github.com/broxus/tycho-types/blob/master/src/models/config/mod.rs#L1117
const param34 = params.get(34);
if (!param20) {
throw new Error('Param (21) state not found');
}
if (!param24) {
throw new Error('Param (25) state not found');
}
if (!param15) {
throw new Error('Param (15) state not found');
}
if (!param34) {
throw new Error('Param (34) state not found');
}
const [data21, data25, data15, data34] = await Promise.all([
this._connection.unpackFromCell({
abiVersion: '2.2',
allowPartial: true,
boc: param20,
structure: PRICES_PARAM_ABI,
}),
this._connection.unpackFromCell({
abiVersion: '2.2',
allowPartial: true,
boc: param24,
structure: FORWARD_FEE_PRICES_PARAM_ABI,
}),
this._connection.unpackFromCell({
abiVersion: '2.2',
allowPartial: true,
boc: param15,
structure: VALIDATOR_CONFIG_ABI,
}),
this._connection.unpackFromCell({
abiVersion: '2.2',
allowPartial: true,
boc: param34,
structure: CUR_VALIDATOR_CONFIG_ABI,
}),
]);
this.setData({
bitPrice: data25.data.value.bitPrice,
blockGasLimit: data21.data.value.blockGasLimit,
cellPrice: data25.data.value.cellPrice,
deleteDueLimit: data21.data.value.deleteDueLimit,
electionsEndBefore: data15.data.value.elections_end_before,
electionsStartBefore: data15.data.value.elections_start_before,
firstFrac: data25.data.value.firstFrac,
flatGasLimit: data21.data.value.flatGasLimit,
flatGasPrice: data21.data.value.flatGasPrice,
freezeDueLimit: data21.data.value.freezeDueLimit,
gasCredit: data21.data.value.gasCredit,
gasLimit: data21.data.value.gasLimit,
gasPrice: data21.data.value.gasPrice,
ihrPriceFactor: data25.data.value.ihrPriceFactor,
lumpPrice: data25.data.value.lumpPrice,
nextFrac: data25.data.value.nextFrac,
specialGasLimit: data21.data.value.specialGasLimit,
stakingDistributionSince: Number(data34.data.value.utime_since),
stakingDistributionUntil: Number(data34.data.value.utime_until),
stakingHeldFor: data15.data.value.stake_held_for,
validatorsElectedFor: data15.data.value.validators_elected_for,
});
}
get bitPrice() {
return this._data.bitPrice;
}
get blockGasLimit() {
return this._data.blockGasLimit;
}
get cellPrice() {
return this._data.cellPrice;
}
get deleteDueLimit() {
return this._data.deleteDueLimit;
}
get electionsEndBefore() {
return this._data.electionsEndBefore;
}
get electionsStartBefore() {
return this._data.electionsStartBefore;
}
get firstFrac() {
return this._data.firstFrac;
}
get flatGasLimit() {
return this._data.flatGasLimit;
}
get flatGasPrice() {
return this._data.flatGasPrice;
}
get freezeDueLimit() {
return this._data.freezeDueLimit;
}
get gasCredit() {
return this._data.gasCredit;
}
get gasLimit() {
return this._data.gasLimit;
}
get gasPrice() {
return this._data.gasPrice;
}
get ihrPriceFactor() {
return this._data.ihrPriceFactor;
}
get lumpPrice() {
return this._data.lumpPrice;
}
get nextFrac() {
return this._data.nextFrac;
}
get specialGasLimit() {
return this._data.specialGasLimit;
}
get stakingDistributionSince() {
return this._data.stakingDistributionSince;
}
get stakingDistributionUntil() {
return this._data.stakingDistributionUntil;
}
get stakingHeldFor() {
return this._data.stakingHeldFor;
}
get validatorsElectedFor() {
return this._data.validatorsElectedFor;
}
get stakingRoundTime() {
return parseInt(BigNumber(this.electionsStartBefore || 0)
.minus(this.electionsEndBefore || 0)
.plus(this.stakingHeldFor || 0)
.toFixed(), 10);
}
}
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Promise)
], MasterChainConfig.prototype, "sync", null);
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", [Function]),
__metadata("design:returntype", Promise)
], MasterChainConfig.prototype, "watch", null);
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], MasterChainConfig.prototype, "unwatch", null);
__decorate([
action.bound,
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", Promise)
], MasterChainConfig.prototype, "syncParams", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "bitPrice", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "blockGasLimit", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "cellPrice", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "deleteDueLimit", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "electionsEndBefore", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "electionsStartBefore", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "firstFrac", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "flatGasLimit", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "flatGasPrice", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "freezeDueLimit", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "gasCredit", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "gasLimit", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "gasPrice", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "ihrPriceFactor", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "lumpPrice", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "nextFrac", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "specialGasLimit", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "stakingDistributionSince", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "stakingDistributionUntil", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "stakingHeldFor", null);
__decorate([
computed,
__metadata("design:type", Object),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "validatorsElectedFor", null);
__decorate([
computed,
__metadata("design:type", Number),
__metadata("design:paramtypes", [])
], MasterChainConfig.prototype, "stakingRoundTime", null);