minter-js-sdk
Version:
JS SDK for Minter Blockchain
411 lines (394 loc) • 18.4 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var _asyncToGenerator = require('@babel/runtime/helpers/asyncToGenerator');
var _typeof = require('@babel/runtime/helpers/typeof');
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var _regeneratorRuntime = require('@babel/runtime/regenerator');
var minterjsUtil = require('minterjs-util');
var getCommissionPrice = require('./get-commission-price.js');
var getPoolInfo = require('./get-pool-info.js');
var replaceCoin = require('./replace-coin.js');
var index = require('../tx-data/index.js');
var tx = require('../tx.js');
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
// import {isCoinId, validateUint} from '../utils.js';
/**
* @enum {string}
*/
var FEE_PRECISION_SETTING = {
// value may be precise, imprecise, or even omitted
AUTO: 'auto',
// precise value is required
PRECISE: 'precise',
// some value is required but it can be imprecise
IMPRECISE: 'imprecise',
OMIT: 'omit'
};
var PRECISION = FEE_PRECISION_SETTING;
/**
* @param {MinterApiInstance} apiInstance
* @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
* @param {import('axios').AxiosRequestConfig} [factoryExtraAxiosOptions]
* @return {EstimateTxCommissionInstance}
*/
function EstimateTxCommission(apiInstance, factoryAxiosOptions, factoryExtraAxiosOptions) {
var getCommissionPrice$1 = getCommissionPrice(apiInstance, factoryExtraAxiosOptions);
var getPoolInfo$1 = getPoolInfo(apiInstance, factoryExtraAxiosOptions);
var replaceCoinSymbol = replaceCoin.ReplaceCoinSymbol(apiInstance, factoryExtraAxiosOptions);
/**
* @typedef {MinterFeeEstimationDirect|MinterFeeEstimationCalculate} MinterFeeEstimation
*/
/**
* @typedef {Function} EstimateTxCommissionInstance
* @param {TxParams|string} txParams
* @param {object} [options]
* @param {FEE_PRECISION_SETTING} [options.needGasCoinFee]
* @param {FEE_PRECISION_SETTING} [options.needBaseCoinFee]
* @param {FEE_PRECISION_SETTING} [options.needPriceCoinFee]
* @param {boolean} [options.loose] - DEPRECATED
* @param {boolean} [options.direct] - DEPRECATED
* @param {import('axios').AxiosRequestConfig} [axiosOptions] - for main request (estimation)
* @param {import('axios').AxiosRequestConfig} [extraAxiosOptions] - for secondary requests (commission price data, coin IDs, and pool info)
* @return {Promise<MinterFeeEstimation>}
*/
return function estimateTxCommission(txParams) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref$needGasCoinFee = _ref.needGasCoinFee,
needGasCoinFee = _ref$needGasCoinFee === void 0 ? PRECISION.AUTO : _ref$needGasCoinFee,
_ref$needBaseCoinFee = _ref.needBaseCoinFee,
needBaseCoinFee = _ref$needBaseCoinFee === void 0 ? PRECISION.AUTO : _ref$needBaseCoinFee,
_ref$needPriceCoinFee = _ref.needPriceCoinFee,
needPriceCoinFee = _ref$needPriceCoinFee === void 0 ? PRECISION.AUTO : _ref$needPriceCoinFee,
loose = _ref.loose,
direct = _ref.direct;
var axiosOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
var extraAxiosOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined;
axiosOptions = _objectSpread(_objectSpread({}, factoryAxiosOptions), axiosOptions);
if (direct !== undefined) {
// eslint-disable-next-line no-console
console.warn('`direct` option in `estimateTxCommission` is deprecated, use `needGasCoinFee`, `needBaseCoinFee`, and `needPriceCoinFee` options instead');
needGasCoinFee = direct ? PRECISION.PRECISE : PRECISION.IMPRECISE;
needBaseCoinFee = direct ? PRECISION.OMIT : PRECISION.IMPRECISE;
needPriceCoinFee = direct ? PRECISION.OMIT : PRECISION.PRECISE;
}
if (loose !== undefined) {
// eslint-disable-next-line no-console
console.warn('`loose` option in `estimateTxCommission` is deprecated, use `needGasCoinFee`, `needBaseCoinFee`, and `needPriceCoinFee` options instead');
needGasCoinFee = !loose ? PRECISION.PRECISE : PRECISION.IMPRECISE;
needBaseCoinFee = !loose ? PRECISION.OMIT : PRECISION.IMPRECISE;
needPriceCoinFee = !loose ? PRECISION.OMIT : PRECISION.PRECISE;
}
var paramsPromise;
if (_typeof(txParams) === 'object') {
/*
if (loose) {
paramsPromise = getCoinId(txParams.gasCoin || 0, txParams.chainId, extraAxiosOptions)
.then((coinId) => {
validateUint(coinId, 'gasCoin');
return {
...txParams,
gasCoin: coinId,
};
});
} else {
*/
// @TODO some fields of tx data can dropped, because they don't affect fee, it will reduce coin id requests and make estimation requests more cacheable
paramsPromise = replaceCoinSymbol(txParams, extraAxiosOptions);
// }
} else {
paramsPromise = Promise.resolve(txParams);
}
return paramsPromise.then(function (updatedTxParams) {
if (_typeof(updatedTxParams) !== 'object') {
return estimateFeeDirect(updatedTxParams, axiosOptions);
} else {
return estimateFeeCalculate(updatedTxParams, {
needGasCoinFee: needGasCoinFee,
needBaseCoinFee: needBaseCoinFee,
needPriceCoinFee: needPriceCoinFee,
axiosOptions: axiosOptions,
extraAxiosOptions: extraAxiosOptions
});
}
});
};
/**
* @typedef {{commission: number|string}} MinterFeeEstimationDirect
*/
/**
* @param {string|TxParams} txParams
* @param {import('axios').AxiosRequestConfig} [axiosOptions]
* @return {Promise<MinterFeeEstimationDirect>}
*/
function estimateFeeDirect(txParams, axiosOptions) {
if (!txParams) {
return Promise.reject(new Error('Transaction not specified'));
}
var tx$1;
if (typeof txParams === 'string') {
tx$1 = txParams;
} else {
tx$1 = tx.prepareTx(_objectSpread(_objectSpread({}, txParams), {}, {
data: index.fillDefaultData(txParams.type, txParams.data)
}), {
disableValidation: true,
disableDecorationParams: true
}).serializeToString();
}
return apiInstance.get("estimate_tx_commission/".concat(tx$1), axiosOptions).then(function (response) {
return {
commission: minterjsUtil.convertFromPip(response.data.commission)
};
});
}
/**
* @typedef {MinterFeeEstimationDirect&{baseCoinCommission: number|string, priceCoinCommission: number|string, commissionPriceData: CommissionPriceData}} MinterFeeEstimationCalculate
*/
/**
* @param {TxParams} txParams
* @param {object} [options]
* @param {FEE_PRECISION_SETTING} [options.needGasCoinFee]
* @param {FEE_PRECISION_SETTING} [options.needBaseCoinFee]
* @param {FEE_PRECISION_SETTING} [options.needPriceCoinFee]
* @param {import('axios').AxiosRequestConfig} [options.axiosOptions]
* @param {import('axios').AxiosRequestConfig} [options.extraAxiosOptions] - applied to secondary requests
* @return {Promise<MinterFeeEstimationCalculate>}
*/
function estimateFeeCalculate(_x, _x2) {
return _estimateFeeCalculate.apply(this, arguments);
}
function _estimateFeeCalculate() {
_estimateFeeCalculate = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(txParams, _ref2) {
var needGasCoinFee, needBaseCoinFee, needPriceCoinFee, axiosOptions, extraAxiosOptions, commissionPriceData, sameGasAndBaseCoins, sameGasAndPriceCoins, samePriceAndBaseCoins, gasDependsOnBase, baseDependsOnGas, priceCoinFee, baseCoinFee, fee, _yield$estimateFeeDir, commission;
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
needGasCoinFee = _ref2.needGasCoinFee, needBaseCoinFee = _ref2.needBaseCoinFee, needPriceCoinFee = _ref2.needPriceCoinFee, axiosOptions = _ref2.axiosOptions, extraAxiosOptions = _ref2.extraAxiosOptions;
if (!(!txParams || _typeof(txParams) !== 'object')) {
_context2.next = 3;
break;
}
throw new TypeError('Invalid txParams');
case 3:
if (needPriceCoinFee === PRECISION.AUTO) {
needPriceCoinFee = PRECISION.OMIT;
}
if (needBaseCoinFee === PRECISION.AUTO) {
needBaseCoinFee = PRECISION.OMIT;
}
if (needGasCoinFee === PRECISION.AUTO) {
needGasCoinFee = PRECISION.PRECISE;
}
if (!(needPriceCoinFee !== PRECISION.OMIT)) {
_context2.next = 12;
break;
}
_context2.next = 9;
return getCommissionPrice$1(extraAxiosOptions);
case 9:
_context2.t0 = _context2.sent;
_context2.next = 13;
break;
case 12:
_context2.t0 = undefined;
case 13:
commissionPriceData = _context2.t0;
// coins may be same only if they both defined
sameGasAndBaseCoins = isGasCoinSameAsBaseCoin(txParams.gasCoin);
sameGasAndPriceCoins = isGasCoinSameAsPriceCoin(txParams.gasCoin, commissionPriceData);
samePriceAndBaseCoins = isPriceCoinSameAsBaseCoin(commissionPriceData);
gasDependsOnBase = sameGasAndBaseCoins && needGasCoinFee === PRECISION.IMPRECISE && needBaseCoinFee === PRECISION.IMPRECISE && commissionPriceData;
baseDependsOnGas = sameGasAndBaseCoins && needGasCoinFee === PRECISION.PRECISE; // priceCoin
priceCoinFee = function () {
// OMIT
if (!commissionPriceData) {
return undefined;
}
// PRECISE
var feePrice = new minterjsUtil.FeePrice(commissionPriceData);
return feePrice.getFeeValue(txParams.type, getFeePriceOptionsFromTxParams(txParams, true));
}(); // baseCoin
_context2.next = 22;
return _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee() {
var priceCoinPool;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
if (!samePriceAndBaseCoins) {
_context.next = 2;
break;
}
return _context.abrupt("return", priceCoinFee);
case 2:
if (!baseDependsOnGas) {
_context.next = 4;
break;
}
return _context.abrupt("return", undefined);
case 4:
if (!(needBaseCoinFee === PRECISION.OMIT)) {
_context.next = 6;
break;
}
return _context.abrupt("return", undefined);
case 6:
if (!(needBaseCoinFee === PRECISION.IMPRECISE && commissionPriceData)) {
_context.next = 11;
break;
}
_context.next = 9;
return getPoolInfo$1(0, commissionPriceData.coin.id, extraAxiosOptions);
case 9:
priceCoinPool = _context.sent;
return _context.abrupt("return", getBaseCoinAmountFromPool(priceCoinFee, priceCoinPool));
case 11:
if (!(needBaseCoinFee === PRECISION.IMPRECISE && !commissionPriceData)) {
_context.next = 13;
break;
}
throw new Error('base coin fee imprecise estimation with omitted price coin is not implemented yet');
case 13:
throw new Error('base coin fee precise estimation not implemented yet');
case 14:
case "end":
return _context.stop();
}
}, _callee);
}))();
case 22:
baseCoinFee = _context2.sent;
if (!sameGasAndPriceCoins) {
_context2.next = 27;
break;
}
// PRECISE
// actual fee may be few pips less than priceCoinFee, assume it's not worth of extra http request
fee = priceCoinFee;
_context2.next = 37;
break;
case 27:
if (!gasDependsOnBase) {
_context2.next = 31;
break;
}
// IMPRECISE
fee = baseCoinFee;
_context2.next = 37;
break;
case 31:
if (!(needGasCoinFee !== PRECISION.OMIT)) {
_context2.next = 37;
break;
}
_context2.next = 34;
return estimateFeeDirect(txParams, axiosOptions);
case 34:
_yield$estimateFeeDir = _context2.sent;
commission = _yield$estimateFeeDir.commission;
fee = commission;
case 37:
if (baseDependsOnGas) {
baseCoinFee = fee;
}
return _context2.abrupt("return", {
commission: fee,
baseCoinCommission: baseCoinFee,
priceCoinCommission: priceCoinFee,
commissionPriceData: commissionPriceData
});
case 39:
case "end":
return _context2.stop();
}
}, _callee2);
}));
return _estimateFeeCalculate.apply(this, arguments);
}
}
/**
* @param {CommissionPriceData} commissionPriceData
* @return {boolean}
*/
function isPriceCoinSameAsBaseCoin(commissionPriceData) {
return Number.parseInt(commissionPriceData === null || commissionPriceData === void 0 ? void 0 : commissionPriceData.coin.id, 10) === 0;
}
/**
* @param {number|string} gasCoinId
* @param {CommissionPriceData} commissionPriceData
* @return {boolean}
*/
function isGasCoinSameAsPriceCoin(gasCoinId, commissionPriceData) {
return Number.parseInt(gasCoinId, 10) === Number.parseInt(commissionPriceData === null || commissionPriceData === void 0 ? void 0 : commissionPriceData.coin.id, 10);
}
/**
* @param {number|string} gasCoinId
* @return {boolean}
*/
function isGasCoinSameAsBaseCoin(gasCoinId) {
return Number.parseInt(gasCoinId, 10) === 0;
}
/**
*
* @param {number|string} priceCoinAmount
* @param {PoolInfo} pool
* @return {string|number}
*/
function getBaseCoinAmountFromPool(priceCoinAmount, pool) {
// amount of base coin in pool
var reserveBase = new minterjsUtil.Big(pool.amount0);
// amount of price coin in pool
var reservePrice = new minterjsUtil.Big(pool.amount1);
// amount of price coin in pool
new minterjsUtil.Big(priceCoinAmount);
// @see https://github.com/MinterTeam/minter-go-node/blob/6e44d5691c9df1a9c725d0f52c5921e8523c7f18/coreV2/state/swap/swap.go#L642
// reserveBase - (reservePrice * reserveBase) / (priceCoinAmount * 0.997 + reservePrice)
var result = reserveBase.minus(reservePrice.times(reserveBase).div(new minterjsUtil.Big(priceCoinAmount).times(0.997).plus(reservePrice)));
// received amount from pool rounds down, spent amount to pool rounds up
// round down
result = result.round(18, minterjsUtil.BIG_ROUND_DOWN);
return result;
}
/**
* @param {TxParams} txParams
* @param {boolean} [disableValidation]
* @return FeePriceOptions
*/
function getFeePriceOptionsFromTxParams(txParams, disableValidation) {
var _txParams$data;
var txType = txParams.type;
if (!txType) {
throw new Error('Tx `type` not specified');
}
var isTickerType = txType === minterjsUtil.TX_TYPE.CREATE_COIN || txType === minterjsUtil.TX_TYPE.CREATE_TOKEN;
var coinSymbol = isTickerType ? (_txParams$data = txParams.data) === null || _txParams$data === void 0 ? void 0 : _txParams$data.symbol : undefined;
if (isTickerType && !coinSymbol && !disableValidation) {
throw new Error('`symbol` not specified for ticker creation tx');
}
var deltaItemCount;
if (txType === minterjsUtil.TX_TYPE.BUY_SWAP_POOL || txType === minterjsUtil.TX_TYPE.SELL_SWAP_POOL || txType === minterjsUtil.TX_TYPE.SELL_ALL_SWAP_POOL) {
var _txParams$data2, _txParams$data2$coins;
var coinCount = (_txParams$data2 = txParams.data) === null || _txParams$data2 === void 0 ? void 0 : (_txParams$data2$coins = _txParams$data2.coins) === null || _txParams$data2$coins === void 0 ? void 0 : _txParams$data2$coins.length;
if (!coinCount && !disableValidation) {
throw new Error('Invalid `coins` field in swap pool tx');
}
// count of pools
deltaItemCount = coinCount - 1;
}
if (txType === minterjsUtil.TX_TYPE.MULTISEND) {
var _txParams$data3, _txParams$data3$list;
// count of recipients
deltaItemCount = (_txParams$data3 = txParams.data) === null || _txParams$data3 === void 0 ? void 0 : (_txParams$data3$list = _txParams$data3.list) === null || _txParams$data3$list === void 0 ? void 0 : _txParams$data3$list.length;
if (!deltaItemCount && !disableValidation) {
throw new Error('Invalid `list` field in multisend tx');
}
}
return {
payload: txParams.payload,
coinSymbol: coinSymbol,
deltaItemCount: deltaItemCount,
fallbackOnInvalidInput: disableValidation
};
}
exports.FEE_PRECISION_SETTING = FEE_PRECISION_SETTING;
exports.default = EstimateTxCommission;