Skip to content

Commit

Permalink
Make baseFeeMultiplier a bignumber
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholaspai committed Jan 3, 2025
1 parent ca24cfc commit 192ac16
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 34 deletions.
14 changes: 8 additions & 6 deletions src/gasPriceOracle/adapters/ethereum.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from "assert";
import { providers } from "ethers";
import { BigNumber, bnZero, getNetworkName } from "../../utils";
import { BigNumber, bnZero, fixedPointAdjustment, getNetworkName } from "../../utils";
import { GasPriceEstimate } from "../types";
import { gasPriceError } from "../util";
import { GasPriceEstimateOptions } from "../oracle";
Expand Down Expand Up @@ -33,7 +33,7 @@ export function eip1559(provider: providers.Provider, opts: GasPriceEstimateOpti
export async function eip1559Raw(
provider: providers.Provider,
chainId: number,
baseFeeMultiplier: number
baseFeeMultiplier: BigNumber
): Promise<GasPriceEstimate> {
const [{ baseFeePerGas }, _maxPriorityFeePerGas] = await Promise.all([
provider.getBlock("pending"),
Expand All @@ -42,7 +42,7 @@ export async function eip1559Raw(
const maxPriorityFeePerGas = BigNumber.from(_maxPriorityFeePerGas);
assert(BigNumber.isBigNumber(baseFeePerGas), `No baseFeePerGas received on ${getNetworkName(chainId)}`);

const scaledBaseFee = baseFeePerGas.mul(baseFeeMultiplier);
const scaledBaseFee = baseFeePerGas.mul(baseFeeMultiplier).div(fixedPointAdjustment);
return {
maxFeePerGas: maxPriorityFeePerGas.add(scaledBaseFee),
maxPriorityFeePerGas,
Expand All @@ -61,7 +61,7 @@ export async function eip1559Raw(
export async function eip1559Bad(
provider: providers.Provider,
chainId: number,
baseFeeMultiplier: number
baseFeeMultiplier: BigNumber
): Promise<GasPriceEstimate> {
const feeData = await provider.getFeeData();

Expand All @@ -70,7 +70,9 @@ export async function eip1559Bad(
});

const maxPriorityFeePerGas = feeData.maxPriorityFeePerGas as BigNumber;
const scaledLastBaseFeePerGas = (feeData.lastBaseFeePerGas as BigNumber).mul(baseFeeMultiplier);
const scaledLastBaseFeePerGas = (feeData.lastBaseFeePerGas as BigNumber)
.mul(baseFeeMultiplier)
.div(fixedPointAdjustment);
const maxFeePerGas = maxPriorityFeePerGas.add(scaledLastBaseFeePerGas);

return { maxPriorityFeePerGas, maxFeePerGas };
Expand All @@ -88,7 +90,7 @@ export async function legacy(provider: providers.Provider, opts: GasPriceEstimat
if (!BigNumber.isBigNumber(gasPrice) || gasPrice.lt(bnZero)) gasPriceError("getGasPrice()", chainId, gasPrice);

return {
maxFeePerGas: gasPrice.mul(baseFeeMultiplier),
maxFeePerGas: gasPrice.mul(baseFeeMultiplier).div(fixedPointAdjustment),
maxPriorityFeePerGas: bnZero,
};
}
4 changes: 3 additions & 1 deletion src/gasPriceOracle/adapters/linea-viem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { estimateGas } from "viem/linea";
import { DEFAULT_SIMULATED_RELAYER_ADDRESS as account } from "../../constants";
import { InternalGasPriceEstimate } from "../types";
import { GasPriceEstimateOptions } from "../oracle";
import { fixedPointAdjustment } from "../../utils";

/**
* @notice The Linea viem provider calls the linea_estimateGas RPC endpoint to estimate gas. Linea is unique
Expand Down Expand Up @@ -32,7 +33,8 @@ export async function eip1559(
value: BigInt(unsignedTx?.value?.toString() ?? "1"),
data: (unsignedTx?.data as Hex) ?? "0x",
});
const priorityFeePerGas = _priorityFeePerGas * BigInt(baseFeeMultiplier);
const priorityFeePerGas =
(_priorityFeePerGas * BigInt(baseFeeMultiplier.toString())) / BigInt(fixedPointAdjustment.toString());

return {
maxFeePerGas: baseFeePerGas + priorityFeePerGas,
Expand Down
4 changes: 2 additions & 2 deletions src/gasPriceOracle/adapters/polygon.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { providers } from "ethers";
import { BaseHTTPAdapter, BaseHTTPAdapterArgs } from "../../priceClient/adapters/baseAdapter";
import { BigNumber, bnZero, isDefined, parseUnits } from "../../utils";
import { BigNumber, bnZero, fixedPointAdjustment, isDefined, parseUnits } from "../../utils";
import { CHAIN_IDs } from "../../constants";
import { GasPriceEstimate } from "../types";
import { gasPriceError } from "../util";
Expand Down Expand Up @@ -112,7 +112,7 @@ export async function gasStation(
// Assume that the maxFeePerGas already includes the priority fee, so back out the priority fee before applying
// the baseFeeMultiplier.
const baseFeeMinusPriorityFee = maxFeePerGas.sub(maxPriorityFeePerGas);
const scaledBaseFee = baseFeeMinusPriorityFee.mul(baseFeeMultiplier);
const scaledBaseFee = baseFeeMinusPriorityFee.mul(baseFeeMultiplier).div(fixedPointAdjustment);
maxFeePerGas = scaledBaseFee.add(maxPriorityFeePerGas);
} catch (err) {
// Fall back to the RPC provider. May be less accurate.
Expand Down
10 changes: 5 additions & 5 deletions src/gasPriceOracle/oracle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import assert from "assert";
import { Transport } from "viem";
import { PopulatedTransaction, providers } from "ethers";
import { CHAIN_IDs } from "../constants";
import { BigNumber, chainIsOPStack } from "../utils";
import { BigNumber, chainIsOPStack, fixedPointAdjustment, toBNWei } from "../utils";
import { GasPriceEstimate } from "./types";
import { getPublicClient } from "./util";
import * as arbitrum from "./adapters/arbitrum";
Expand All @@ -13,7 +13,7 @@ import * as lineaViem from "./adapters/linea-viem";

export interface GasPriceEstimateOptions {
// baseFeeMultiplier Multiplier applied to base fee for EIP1559 gas prices (or total fee for legacy).
baseFeeMultiplier: number;
baseFeeMultiplier: BigNumber;
// legacyFallback In the case of an unrecognized chain, fall back to type 0 gas estimation.
legacyFallback: boolean;
// chainId The chain ID to query for gas prices. If omitted can be inferred by provider.
Expand All @@ -25,7 +25,7 @@ export interface GasPriceEstimateOptions {
}

const GAS_PRICE_ESTIMATE_DEFAULTS = {
baseFeeMultiplier: 1,
baseFeeMultiplier: toBNWei("1"),
legacyFallback: true,
};

Expand All @@ -41,7 +41,7 @@ export async function getGasPriceEstimate(
): Promise<GasPriceEstimate> {
const baseFeeMultiplier = opts.baseFeeMultiplier ?? GAS_PRICE_ESTIMATE_DEFAULTS.baseFeeMultiplier;
assert(
baseFeeMultiplier >= 1.0 && baseFeeMultiplier <= 5,
baseFeeMultiplier.gte(toBNWei("1.0")) && baseFeeMultiplier.lte(toBNWei("5")),
`Require 1.0 < base fee multiplier (${baseFeeMultiplier}) <= 5.0 for a total gas multiplier within [+1.0, +5.0]`
);
const chainId = opts.chainId ?? (await provider.getNetwork()).chainId;
Expand Down Expand Up @@ -117,7 +117,7 @@ export async function _getViemGasPriceEstimate(
let gasPrice: bigint | undefined;
({ maxFeePerGas, maxPriorityFeePerGas, gasPrice } = await viemProvider.estimateFeesPerGas());

maxFeePerGas ??= gasPrice! * BigInt(baseFeeMultiplier);
maxFeePerGas ??= (gasPrice! * BigInt(baseFeeMultiplier.toString())) / BigInt(fixedPointAdjustment.toString());
maxPriorityFeePerGas ??= BigInt(0);
}

Expand Down
3 changes: 2 additions & 1 deletion src/relayFeeCalculator/chain-queries/baseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
TransactionCostEstimate,
estimateTotalGasRequiredByUnsignedTransaction,
populateV3Relay,
BigNumber,
} from "../../utils";
import { Logger, QueryInterface } from "../relayFeeCalculator";
import { Transport } from "viem";
Expand Down Expand Up @@ -69,7 +70,7 @@ export class QueryBase implements QueryInterface {
options: Partial<{
gasPrice: BigNumberish;
gasUnits: BigNumberish;
baseFeeMultiplier: number;
baseFeeMultiplier: BigNumber;
transport: Transport;
}> = {}
): Promise<TransactionCostEstimate> {
Expand Down
2 changes: 1 addition & 1 deletion src/relayFeeCalculator/relayFeeCalculator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface QueryInterface {
options?: Partial<{
gasPrice: BigNumberish;
gasUnits: BigNumberish;
baseFeeMultiplier: number;
baseFeeMultiplier: BigNumber;
transport: Transport;
}>
) => Promise<TransactionCostEstimate>;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,11 @@ export async function estimateTotalGasRequiredByUnsignedTransaction(
options: Partial<{
gasPrice: BigNumberish;
gasUnits: BigNumberish;
baseFeeMultiplier: number;
baseFeeMultiplier: BigNumber;
transport: Transport;
}> = {}
): Promise<TransactionCostEstimate> {
const { gasPrice: _gasPrice, gasUnits, baseFeeMultiplier = 1.0, transport } = options || {};
const { gasPrice: _gasPrice, gasUnits, baseFeeMultiplier = toBNWei("1"), transport } = options || {};

const { chainId } = await provider.getNetwork();
const voidSigner = new VoidSigner(senderAddress, provider);
Expand Down
43 changes: 27 additions & 16 deletions test/GasPriceOracle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import dotenv from "dotenv";
import { encodeFunctionData } from "viem";
import { getGasPriceEstimate } from "../src/gasPriceOracle";
import { BigNumber, bnZero, parseUnits } from "../src/utils";
import { BigNumber, bnZero, fixedPointAdjustment, parseUnits, toBNWei } from "../src/utils";
import { assertPromiseError, expect, makeCustomTransport, randomAddress } from "../test/utils";
import { MockedProvider } from "./utils/provider";
import { MockPolygonGasStationBaseFee, MockPolygonGasStationPriorityFee } from "../src/gasPriceOracle/adapters/polygon";
Expand Down Expand Up @@ -46,15 +46,15 @@ describe("Gas Price Oracle", function () {
await assertPromiseError(
getGasPriceEstimate(provider, {
chainId: 1,
baseFeeMultiplier: 0.5,
baseFeeMultiplier: toBNWei("0.5"),
}),
"base fee multiplier"
);
// Too high:
await assertPromiseError(
getGasPriceEstimate(provider, {
chainId: 1,
baseFeeMultiplier: 5.5,
baseFeeMultiplier: toBNWei("5.5"),
}),
"base fee multiplier"
);
Expand All @@ -73,7 +73,7 @@ describe("Gas Price Oracle", function () {
chainId,
transport: customTransport,
unsignedTx,
baseFeeMultiplier: 2.0,
baseFeeMultiplier: toBNWei("2.0"),
});

// For Linea, base fee is expected to be hardcoded and unaffected by the base fee multiplier while
Expand All @@ -93,7 +93,7 @@ describe("Gas Price Oracle", function () {
const { maxFeePerGas, maxPriorityFeePerGas } = await getGasPriceEstimate(provider, {
chainId,
transport: customTransport,
baseFeeMultiplier: 2.0,
baseFeeMultiplier: toBNWei("2.0"),
});

// For Linea, base fee is expected to be hardcoded and unaffected by the base fee multiplier while
Expand All @@ -104,7 +104,7 @@ describe("Gas Price Oracle", function () {
delete process.env[chainKey];
});
it("Ethers gas price retrieval", async function () {
const baseFeeMultiplier = 2.0;
const baseFeeMultiplier = toBNWei("2.0");
for (const chainId of ethersProviderChainIds) {
const { maxFeePerGas: markedUpMaxFeePerGas, maxPriorityFeePerGas: markedUpMaxPriorityFeePerGas } =
await getGasPriceEstimate(provider, { chainId, baseFeeMultiplier });
Expand All @@ -130,7 +130,7 @@ describe("Gas Price Oracle", function () {
}
});
it("Ethers EIP1559 Raw", async function () {
const baseFeeMultiplier = 2.0;
const baseFeeMultiplier = toBNWei("2.0");
const chainId = 1;
const chainKey = `GAS_PRICE_EIP1559_RAW_${chainId}`;
process.env[chainKey] = "true";
Expand All @@ -140,7 +140,10 @@ describe("Gas Price Oracle", function () {

// Base fee should be multiplied by multiplier. Returned max fee includes priority fee
// so back it out before scaling.
const expectedMarkedUpMaxFeePerGas = stdLastBaseFeePerGas.mul(baseFeeMultiplier).add(stdMaxPriorityFeePerGas);
const expectedMarkedUpMaxFeePerGas = stdLastBaseFeePerGas
.mul(baseFeeMultiplier)
.div(fixedPointAdjustment)
.add(stdMaxPriorityFeePerGas);
expect(markedUpMaxFeePerGas).to.equal(expectedMarkedUpMaxFeePerGas);

// Priority fees should be the same
Expand All @@ -150,22 +153,25 @@ describe("Gas Price Oracle", function () {
it("Ethers EIP1559 Bad", async function () {
// This test should return identical results to the Raw test but it makes different
// provider calls, so we're really testing that the expected provider functions are called.
const baseFeeMultiplier = 2.0;
const baseFeeMultiplier = toBNWei("2.0");
const chainId = 1;

const { maxFeePerGas: markedUpMaxFeePerGas, maxPriorityFeePerGas: markedUpMaxPriorityFeePerGas } =
await getGasPriceEstimate(provider, { chainId, baseFeeMultiplier });

// Base fee should be multiplied by multiplier. Returned max fee includes priority fee
// so back it out before scaling.
const expectedMarkedUpMaxFeePerGas = stdLastBaseFeePerGas.mul(baseFeeMultiplier).add(stdMaxPriorityFeePerGas);
const expectedMarkedUpMaxFeePerGas = stdLastBaseFeePerGas
.mul(baseFeeMultiplier)
.div(fixedPointAdjustment)
.add(stdMaxPriorityFeePerGas);
expect(markedUpMaxFeePerGas).to.equal(expectedMarkedUpMaxFeePerGas);

// Priority fees should be the same
expect(markedUpMaxPriorityFeePerGas).to.equal(stdMaxPriorityFeePerGas);
});
it("Ethers Legacy", async function () {
const baseFeeMultiplier = 2.0;
const baseFeeMultiplier = toBNWei("2.0");
const chainId = 324;

const { maxFeePerGas: markedUpMaxFeePerGas, maxPriorityFeePerGas: markedUpMaxPriorityFeePerGas } =
Expand All @@ -174,14 +180,14 @@ describe("Gas Price Oracle", function () {
// Legacy gas price is equal to base fee + priority fee and the full amount
// should be multiplied since the RPC won't return the broken down fee.
const expectedGasPrice = stdLastBaseFeePerGas.add(stdMaxPriorityFeePerGas);
const expectedMarkedUpMaxFeePerGas = expectedGasPrice.mul(baseFeeMultiplier);
const expectedMarkedUpMaxFeePerGas = expectedGasPrice.mul(baseFeeMultiplier).div(fixedPointAdjustment);
expect(expectedMarkedUpMaxFeePerGas).to.equal(markedUpMaxFeePerGas);

// Priority fees should be zero
expect(markedUpMaxPriorityFeePerGas).to.equal(0);
});
it("Ethers Polygon GasStation", async function () {
const baseFeeMultiplier = 2.0;
const baseFeeMultiplier = toBNWei("2.0");
process.env["TEST_POLYGON_GAS_STATION"] = "true";
const chainId = 137;
const { maxFeePerGas, maxPriorityFeePerGas } = await getGasPriceEstimate(provider, {
Expand All @@ -190,13 +196,16 @@ describe("Gas Price Oracle", function () {
});

expect(maxFeePerGas).to.equal(
MockPolygonGasStationBaseFee().mul(baseFeeMultiplier).add(MockPolygonGasStationPriorityFee())
MockPolygonGasStationBaseFee()
.mul(baseFeeMultiplier)
.div(fixedPointAdjustment)
.add(MockPolygonGasStationPriorityFee())
);
expect(maxPriorityFeePerGas).to.equal(MockPolygonGasStationPriorityFee());
delete process.env["TEST_POLYGON_GAS_STATION"];
});
it("Ethers Polygon GasStation: Fallback", async function () {
const baseFeeMultiplier = 2.0;
const baseFeeMultiplier = toBNWei("2.0");
process.env["TEST_REVERTING_POLYGON_GAS_STATION"] = "true";
const chainId = 137;

Expand All @@ -211,7 +220,9 @@ describe("Gas Price Oracle", function () {
const expectedPriorityFee = stdMaxPriorityFeePerGas.gt(minPolygonPriorityFee)
? stdMaxPriorityFeePerGas
: minPolygonPriorityFee;
expect(maxFeePerGas).to.equal(stdLastBaseFeePerGas.mul(baseFeeMultiplier).add(expectedPriorityFee));
expect(maxFeePerGas).to.equal(
stdLastBaseFeePerGas.mul(baseFeeMultiplier).div(fixedPointAdjustment).add(expectedPriorityFee)
);
expect(maxPriorityFeePerGas).to.equal(expectedPriorityFee);
delete process.env["TEST_REVERTING_POLYGON_GAS_STATION"];
});
Expand Down

0 comments on commit 192ac16

Please sign in to comment.