From 8ad4618ba2f098c3fae34dd6d49e9fa261d1e69c Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:35:05 +0100 Subject: [PATCH 1/3] accept 20% below preVerificationGas on arbitrum --- src/utils/validation.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 1c8c6a76..88d88f52 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -330,6 +330,7 @@ export async function calcPreVerificationGas( preVerificationGas = await calcArbitrumPreVerificationGas( publicClient, userOperation, + validate, entryPoint, preVerificationGas ) @@ -582,6 +583,7 @@ const getArbitrumL1FeeAbi = [ export async function calcArbitrumPreVerificationGas( publicClient: PublicClient, op: UserOperation, + validate: boolean, entryPoint: Address, staticFee: bigint ) { @@ -649,7 +651,13 @@ export async function calcArbitrumPreVerificationGas( serializedTx ]) - return result[0] + staticFee + let gasForL1 = result[0] + + if (validate) { + gasForL1 = (gasForL1 * 80n) / 100n + } + + return staticFee + gasForL1 } export function parseViemError(err: unknown) { From 2cfd4b2435d9ce9e8d9bb6b99741c50b778852d9 Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:10:05 +0100 Subject: [PATCH 2/3] fix arbitrum preVerificationGas calculation logic --- src/handlers/gasPriceManager.ts | 76 ++++++++++++++++++++++++++++++++- src/utils/validation.ts | 25 ++++++++--- 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/src/handlers/gasPriceManager.ts b/src/handlers/gasPriceManager.ts index d5964ea8..00cdb66c 100644 --- a/src/handlers/gasPriceManager.ts +++ b/src/handlers/gasPriceManager.ts @@ -6,7 +6,7 @@ import { } from "@alto/types" import { maxBigInt, minBigInt, type Logger } from "@alto/utils" import * as sentry from "@sentry/node" -import { parseGwei, type Chain, type PublicClient } from "viem" +import { parseGwei, type Chain, type PublicClient, maxUint128 } from "viem" import { celo, celoAlfajores, @@ -36,6 +36,77 @@ function getGasStationUrl(chainId: ChainId.Polygon | ChainId.Mumbai): string { } } +class ArbitrumManager { + private queueL1BaseFee: { timestamp: number; baseFee: bigint }[] + private queueL2BaseFee: { timestamp: number; baseFee: bigint }[] + + private maxQueueSize + private queueValidity = 15_000 + + constructor(maxQueueSize: number) { + this.maxQueueSize = maxQueueSize + this.queueL1BaseFee = [] + this.queueL2BaseFee = [] + } + + public saveL1BaseFee(baseFee: bigint) { + const queue = this.queueL1BaseFee + const last = queue.length > 0 ? queue[queue.length - 1] : null + const timestamp = Date.now() + + if (!last || timestamp - last.timestamp >= this.queueValidity) { + if (queue.length >= this.maxQueueSize) { + queue.shift() + } + queue.push({ baseFee, timestamp }) + } else if (baseFee < last.baseFee) { + last.baseFee = baseFee + last.timestamp = timestamp + } + } + + public saveL2BaseFee(baseFee: bigint) { + const queue = this.queueL2BaseFee + const last = queue.length > 0 ? queue[queue.length - 1] : null + const timestamp = Date.now() + + if (!last || timestamp - last.timestamp >= this.queueValidity) { + if (queue.length >= this.maxQueueSize) { + queue.shift() + } + queue.push({ baseFee, timestamp }) + } else if (baseFee < last.baseFee) { + last.baseFee = baseFee + last.timestamp = timestamp + } + } + + public async getMinL1BaseFee() { + const queue = this.queueL1BaseFee + + if (queue.length === 0) { + return 1n + } + return queue.reduce( + (acc: bigint, cur) => minBigInt(cur.baseFee, acc), + queue[0].baseFee + ) + } + + public async getMaxL2BaseFee() { + const queue = this.queueL2BaseFee + + if (queue.length === 0) { + return maxUint128 + } + + return queue.reduce( + (acc: bigint, cur) => maxBigInt(cur.baseFee, acc), + queue[0].baseFee + ) + } +} + export class GasPriceManager { private chain: Chain private publicClient: PublicClient @@ -53,6 +124,7 @@ export class GasPriceManager { private gasBumpMultiplier: bigint private gasPriceRefreshIntervalInSeconds: number private chainType: ChainType + public arbitrumManager: ArbitrumManager constructor( chain: Chain, @@ -83,6 +155,8 @@ export class GasPriceManager { this.updateGasPrice() }, this.gasPriceRefreshIntervalInSeconds * 1000) } + + this.arbitrumManager = new ArbitrumManager(this.maxQueueSize) } public init() { diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 88d88f52..69b9c113 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -330,9 +330,10 @@ export async function calcPreVerificationGas( preVerificationGas = await calcArbitrumPreVerificationGas( publicClient, userOperation, - validate, entryPoint, - preVerificationGas + preVerificationGas, + gasPriceManager, + validate ) } @@ -583,9 +584,10 @@ const getArbitrumL1FeeAbi = [ export async function calcArbitrumPreVerificationGas( publicClient: PublicClient, op: UserOperation, - validate: boolean, entryPoint: Address, - staticFee: bigint + staticFee: bigint, + gasPriceManager: GasPriceManager, + validate: boolean ) { let selector: Hex let paramData: Hex @@ -651,10 +653,21 @@ export async function calcArbitrumPreVerificationGas( serializedTx ]) - let gasForL1 = result[0] + let [gasForL1, l2BaseFee, l1BaseFeeEstimate] = result + + gasPriceManager.arbitrumManager.saveL1BaseFee(l1BaseFeeEstimate) + gasPriceManager.arbitrumManager.saveL2BaseFee(l2BaseFee) if (validate) { - gasForL1 = (gasForL1 * 80n) / 100n + // gasEstimateL1Component source: https://github.com/OffchainLabs/nitro/blob/5cd7d6913eb6b4dedb08f6ea49d7f9802d2cc5b9/execution/nodeInterface/NodeInterface.go#L515-L551 + const feesForL1 = (gasForL1 * l2BaseFee) / l1BaseFeeEstimate + + const minL1BaseFeeEstimate = + await gasPriceManager.arbitrumManager.getMinL1BaseFee() + const maxBaseFee = + await gasPriceManager.arbitrumManager.getMaxL2BaseFee() + + gasForL1 = (feesForL1 * minL1BaseFeeEstimate) / maxBaseFee } return staticFee + gasForL1 From f71fec1c894464c585e19d4f73be65f9531536af Mon Sep 17 00:00:00 2001 From: mouseless <97399882+mouseless-eth@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:19:03 +0100 Subject: [PATCH 3/3] nit on naming --- src/utils/validation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/validation.ts b/src/utils/validation.ts index 69b9c113..b36cdccc 100644 --- a/src/utils/validation.ts +++ b/src/utils/validation.ts @@ -664,10 +664,10 @@ export async function calcArbitrumPreVerificationGas( const minL1BaseFeeEstimate = await gasPriceManager.arbitrumManager.getMinL1BaseFee() - const maxBaseFee = + const maxL2BaseFee = await gasPriceManager.arbitrumManager.getMaxL2BaseFee() - gasForL1 = (feesForL1 * minL1BaseFeeEstimate) / maxBaseFee + gasForL1 = (feesForL1 * minL1BaseFeeEstimate) / maxL2BaseFee } return staticFee + gasForL1