UNPKG

@rarible/estimate-middleware

Version:
116 lines (115 loc) 4.66 kB
import { __awaiter } from "tslib"; import { createAsyncMiddleware, getUniqueId } from "json-rpc-engine"; import { toBn } from "@rarible/utils/bn/index"; import { extractError, RpcError } from "./utils.js"; /** * Creates async middleware for gas estimation if gas not defined * @param engine JsonRpcEngine to use for gas estimation * @param force set true if estimate tx even if gas is provided */ export function createEstimateGasMiddleware(engine, force = false, threshold = 1.03, multiplier = 2) { return createAsyncMiddleware((req, res, next) => __awaiter(this, void 0, void 0, function* () { if (req.method === "eth_subscribe") { res.error = new RpcError("Notifications not supported", -32000); return next(); } if (req.method === "eth_sendTransaction") { try { const params = getTransactionParams(req); if (force || !params.gas) { const gasLimitResponse = yield engine.handle({ jsonrpc: "2.0", id: getUniqueId(), params: [getEstimateParams(params)], method: "eth_estimateGas", }); const limitRaw = handleHexResponse(gasLimitResponse); if (limitRaw) { const limitHex = extractHex(limitRaw); const multiplied = toBn(limitHex, 16).multipliedBy(threshold).toFixed(0); params["gas"] = withPrefix(toBn(multiplied).toString(16)); } const maxPriorityFeePerGasResponse = yield engine.handle({ jsonrpc: "2.0", id: getUniqueId(), params: [], method: "eth_maxPriorityFeePerGas", }); const maxPriorityFeePerGasResponseRaw = handleHexResponse(maxPriorityFeePerGasResponse); const blockResponse = yield engine.handle({ jsonrpc: "2.0", id: getUniqueId(), params: ["pending", false], method: "eth_getBlockByNumber", }); const baseFeeRaw = extractBaseFeePerGas(blockResponse); if (maxPriorityFeePerGasResponseRaw && baseFeeRaw) { if (params.gasPrice !== undefined) { delete params.gasPrice; } const baseFee = toBn(extractHex(baseFeeRaw), 16).multipliedBy(multiplier).toFixed(0); const maxPriorityFeePerGas = extractHex(maxPriorityFeePerGasResponseRaw); const maxFeePerGasHex = toBn(maxPriorityFeePerGas, 16).plus(baseFee).toString(16); params["maxPriorityFeePerGas"] = maxPriorityFeePerGasResponseRaw; params["maxFeePerGas"] = withPrefix(maxFeePerGasHex); } } } catch (error) { res.error = extractError(error); } } return next(); })); } function withPrefix(value) { return `0x${value}`; } function extractHex(value) { return value.startsWith("0x") ? value.substring(2) : value; } function handleHexResponse(response) { if (isJSONRpcResponse(response)) { if (response.error) { throw response.error; } if (typeof response.result === "string") { return response.result; } } throw new RpcError("Can't handle JSON rpc response", -32700); } function extractBaseFeePerGas(response) { if (isJSONRpcResponse(response)) { if (response.error) { throw response.error; } if (typeof response.result === "object") { return response.result.baseFeePerGas; } } throw new RpcError("Can't handle JSON rpc response", -32700); } function getTransactionParams(request) { if (request.params) { const [tx] = request.params; if (isSendParams(tx)) { return tx; } } throw new RpcError("Can't parse eth_sendTransaction params", -32600); } function isJSONRpcResponse(x) { return typeof x === "object" && x !== null && "jsonrpc" in x && "id" in x; } function isSendParams(x) { return typeof x === "object" && x !== null && "to" in x; } function getEstimateParams(params) { return { to: params.to, data: params.data, value: params.value, from: params.from, }; }