@rarible/estimate-middleware
Version:
116 lines (115 loc) • 4.66 kB
JavaScript
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,
};
}