minter-js-sdk
Version:
JS SDK for Minter Blockchain
262 lines (245 loc) • 11.7 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var _slicedToArray = require('@babel/runtime/helpers/slicedToArray');
var _defineProperty = require('@babel/runtime/helpers/defineProperty');
var _objectWithoutProperties = require('@babel/runtime/helpers/objectWithoutProperties');
var minterjsUtil = require('minterjs-util');
var postSignedTx = require('./post-signed-tx.js');
var getNonce = require('./get-nonce.js');
var replaceCoin = require('./replace-coin.js');
var tx = require('../tx.js');
var utils = require('../utils.js');
var _excluded = ["gasRetryLimit", "nonceRetryLimit", "mempoolRetryLimit"],
_excluded2 = ["gasRetryLimit", "nonceRetryLimit", "mempoolRetryLimit"];
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; }
/**
* @typedef {TxOptions & PostTxOptionsExtra} PostTxOptions
* @typedef {object} PostTxOptionsExtra
* @property {number} [gasRetryLimit=2] - max number of autofix retries after gas error
* @property {number} [nonceRetryLimit=0] - max number of autofix retries after nonce error
* @preserve {number} [mempoolRetryLimit=0] - max number of retries after "already exists in mempool" error
*/
/**
* @param {MinterApiInstance} apiInstance
* @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
* @param {import('axios').AxiosRequestConfig} [factoryExtraAxiosOptions]
* @return {PostTxInstance}
*/
function PostTx(apiInstance, factoryAxiosOptions, factoryExtraAxiosOptions) {
var ensureNonce = new EnsureNonce(apiInstance, factoryExtraAxiosOptions);
var replaceCoinSymbol = new replaceCoin.ReplaceCoinSymbol(apiInstance, factoryExtraAxiosOptions);
/**
* @typedef {Function} PostTxInstance
* @param {TxParams} txParams
* @param {PostTxOptions} options
* @param {import('axios').AxiosRequestConfig} [axiosOptions]
* @param {import('axios').AxiosRequestConfig} [extraAxiosOptions] - applied to secondary requests
* @return {Promise<PostTxResponse>}
*/
return function postTx(txParams) {
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
_ref$gasRetryLimit = _ref.gasRetryLimit,
gasRetryLimit = _ref$gasRetryLimit === void 0 ? 2 : _ref$gasRetryLimit,
_ref$nonceRetryLimit = _ref.nonceRetryLimit,
nonceRetryLimit = _ref$nonceRetryLimit === void 0 ? 0 : _ref$nonceRetryLimit,
_ref$mempoolRetryLimi = _ref.mempoolRetryLimit,
mempoolRetryLimit = _ref$mempoolRetryLimi === void 0 ? 0 : _ref$mempoolRetryLimi,
txOptions = _objectWithoutProperties(_ref, _excluded);
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 (!txOptions.privateKey && txParams.privateKey) {
txOptions.privateKey = txParams.privateKey;
// eslint-disable-next-line no-console
console.warn('privateKey field in tx params is deprecated, pass it to the second parameter');
}
// @TODO asserts
var privateKeyPromise;
if (txOptions.privateKey) {
privateKeyPromise = Promise.resolve(txOptions.privateKey);
} else if (txOptions.seedPhrase) {
privateKeyPromise = utils.getPrivateKeyFromSeedPhraseAsync(txOptions.seedPhrase);
} else {
privateKeyPromise = Promise.resolve(undefined);
}
return privateKeyPromise.then(function (privateKey) {
return Promise.all([ensureNonce(txParams, _objectSpread(_objectSpread({}, txOptions), {}, {
privateKey: privateKey
}), extraAxiosOptions), replaceCoinSymbol(txParams, extraAxiosOptions), Promise.resolve(privateKey)]);
}).then(function (_ref2) {
var _ref3 = _slicedToArray(_ref2, 3),
newNonce = _ref3[0],
newTxParams = _ref3[1],
privateKey = _ref3[2];
return _postTxHandleErrors(apiInstance, _objectSpread(_objectSpread({}, newTxParams), {}, {
nonce: newNonce
}), _objectSpread(_objectSpread({
gasRetryLimit: gasRetryLimit,
nonceRetryLimit: nonceRetryLimit,
mempoolRetryLimit: mempoolRetryLimit
}, txOptions), {}, {
privateKey: privateKey,
axiosOptions: axiosOptions
}));
});
};
}
/**
* @param {MinterApiInstance} apiInstance
* @param {TxParams} txParams
* @param {TxOptions} [options]
* @param {import('axios').AxiosRequestConfig} [axiosOptions]
* @return {Promise<PostTxResponse>}
*/
function _postTx(apiInstance, txParams, options, axiosOptions) {
if (!txParams.chainId && apiInstance.defaults.chainId) {
txParams.chainId = apiInstance.defaults.chainId;
}
var tx$1;
tx$1 = !txParams.signatureData && utils.toInteger(txParams.signatureType) !== '2' ? tx.default(txParams, options) : tx.prepareTx(txParams, options);
return new postSignedTx(apiInstance)(tx$1.serializeToString(), axiosOptions);
}
/**
* Send `_postTx()` request and if it fails because of too low gas or already exists in mempool - make retries
* On gas retry `txParams.gasPrice` will be updated with required min gas value from error response.
* On mempool retry request will be sent after 5 seconds (average time of a block) to try put transaction into next block
* @param {MinterApiInstance} apiInstance
* @param {TxParams} txParams
* @param {PostTxOptions} options
* @param {import('axios').AxiosRequestConfig} [axiosOptions]
* @return {Promise<PostTxResponse>}
*/
function _postTxHandleErrors(apiInstance, txParams, options, axiosOptions) {
var gasRetryLimit = options.gasRetryLimit,
nonceRetryLimit = options.nonceRetryLimit,
mempoolRetryLimit = options.mempoolRetryLimit,
txOptions = _objectWithoutProperties(options, _excluded2);
return _postTx(apiInstance, txParams, txOptions, axiosOptions)["catch"](function (error) {
// @TODO limit max gas_price to prevent sending tx with to high fees
if (utils.toInteger(txParams.signatureType) !== '2' && gasRetryLimit > 0 && isGasError(error)) {
var minGas = getMinGasFromError(error);
// eslint-disable-next-line no-console
console.log("make postTx retry, old gasPrice ".concat(txParams.gasPrice, ", new gasPrice ").concat(minGas));
return _postTxHandleErrors(apiInstance, _objectSpread(_objectSpread({}, txParams), {}, {
gasPrice: minGas
}), _objectSpread(_objectSpread({}, options), {}, {
gasRetryLimit: gasRetryLimit - 1
}), axiosOptions);
} else if (utils.toInteger(txParams.signatureType) !== '2' && nonceRetryLimit > 0 && isNonceError(error)) {
var newNonce = getNonceFromError(error);
// eslint-disable-next-line no-console
console.log("make postTx retry, old nonce ".concat(txParams.nonce, ", new nonce ").concat(newNonce));
return _postTxHandleErrors(apiInstance, _objectSpread(_objectSpread({}, txParams), {}, {
nonce: newNonce
}), _objectSpread(_objectSpread({}, options), {}, {
nonceRetryLimit: nonceRetryLimit - 1
}), axiosOptions);
} else if (mempoolRetryLimit > 0 && isMempoolError(error)) {
// eslint-disable-next-line no-console
console.log('make postTx retry: tx exists in mempool');
return utils.wait(5000).then(function () {
return _postTxHandleErrors(apiInstance, txParams, _objectSpread(_objectSpread({}, options), {}, {
mempoolRetryLimit: mempoolRetryLimit - 1
}), axiosOptions);
});
} else {
throw error;
}
});
}
/**
* @param {MinterApiInstance} apiInstance
* @param {import('axios').AxiosRequestConfig} [factoryAxiosOptions]
* @return {EnsureNonceInstance}
*/
function EnsureNonce(apiInstance, factoryAxiosOptions) {
var getNonce$1 = new getNonce(apiInstance, factoryAxiosOptions);
/**
* @typedef {Function} EnsureNonceInstance
* @param {TxParams} txParams
* @param {object} [txOptions]
* @param {ByteArray} [txOptions.privateKey]
* @param {string} [txOptions.address]
* @param {string} [txOptions.seedPhrase]
* @param {import('axios').AxiosRequestConfig} [axiosOptions]
* @return {Promise<number>}
*/
return function ensureNonce(txParams) {
var _ref4 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
privateKey = _ref4.privateKey,
address = _ref4.address;
_ref4.seedPhrase;
var axiosOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
var nonce = txParams.nonce;
if (!nonce && !address && !privateKey) {
throw new Error('No nonce is given and no address or privateKey to retrieve it from API');
}
if (nonce) {
return Promise.resolve(nonce);
}
// @TODO seedPhrase not used
if (privateKey) {
var privateKeyBuffer = utils.bufferFromBytes(privateKey);
address = minterjsUtil.privateToAddressString(privateKeyBuffer);
}
return getNonce$1(address, axiosOptions);
};
}
/**
* Get tx_result data from error
* @param error
* @return {object|undefined}
*/
// function getTxResult(error) {
// error = error.response?.data?.error;
// // gate moves tx_result into root error, so check it too
// return error && (error.tx_result || error);
// }
/**
* Check if error caused by too low gas
* @param {AxiosError} error
* @return {boolean}
*/
function isGasError(error) {
var _error$response;
return ((_error$response = error.response) === null || _error$response === void 0 ? void 0 : _error$response.data.error.code) === '114';
}
/**
* Check if error caused by: "Tx from address already exists in mempool"
* @param {AxiosError} error
* @return {boolean}
*/
function isMempoolError(error) {
var _error$response2;
return ((_error$response2 = error.response) === null || _error$response2 === void 0 ? void 0 : _error$response2.data.error.code) === '113';
}
/**
* Check if error caused by nonce
* @param {AxiosError} error
* @return {boolean}
*/
function isNonceError(error) {
var _error$response3;
return ((_error$response3 = error.response) === null || _error$response3 === void 0 ? void 0 : _error$response3.data.error.code) === '101';
}
/**
* Retrieve required min gas value from error message
* @param {AxiosError} error
* @return {number}
*/
function getMinGasFromError(error) {
var _error$response4;
return Number((_error$response4 = error.response) === null || _error$response4 === void 0 ? void 0 : _error$response4.data.error.data.min_gas_price);
}
/**
* Retrieve required min gas value from error message
* @param {AxiosError} error
* @return {number}
*/
function getNonceFromError(error) {
var _error$response5;
return Number((_error$response5 = error.response) === null || _error$response5 === void 0 ? void 0 : _error$response5.data.error.data.expected_nonce);
}
exports.EnsureNonce = EnsureNonce;
exports.default = PostTx;