UNPKG

minter-js-sdk

Version:
262 lines (245 loc) 11.7 kB
'use strict'; 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;