diff --git a/.changeset/fair-bees-promise.md b/.changeset/fair-bees-promise.md new file mode 100644 index 0000000000000..243fbb7227dd5 --- /dev/null +++ b/.changeset/fair-bees-promise.md @@ -0,0 +1,5 @@ +--- +'@eth-optimism/batch-submitter-service': patch +--- + +Add 20% buffer to gas estimation on tx-batch submission to prevent OOG reverts diff --git a/go/batch-submitter/drivers/sequencer/driver.go b/go/batch-submitter/drivers/sequencer/driver.go index 0c5e7a8b74402..e9133d1ab290f 100644 --- a/go/batch-submitter/drivers/sequencer/driver.go +++ b/go/batch-submitter/drivers/sequencer/driver.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum-optimism/optimism/go/bss-core/metrics" "github.com/ethereum-optimism/optimism/go/bss-core/txmgr" l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" @@ -267,6 +268,46 @@ func (d *Driver) UpdateGasPrice( tx *types.Transaction, ) (*types.Transaction, error) { + gasTipCap, err := d.cfg.L1Client.SuggestGasTipCap(ctx) + if err != nil { + // If the transaction failed because the backend does not support + // eth_maxPriorityFeePerGas, fallback to using the default constant. + // Currently Alchemy is the only backend provider that exposes this + // method, so in the event their API is unreachable we can fallback to a + // degraded mode of operation. This also applies to our test + // environments, as hardhat doesn't support the query either. + if !drivers.IsMaxPriorityFeePerGasNotFoundError(err) { + return nil, err + } + + log.Warn(d.cfg.Name + " eth_maxPriorityFeePerGas is unsupported " + + "by current backend, using fallback gasTipCap") + gasTipCap = drivers.FallbackGasTipCap + } + + header, err := d.cfg.L1Client.HeaderByNumber(ctx, nil) + if err != nil { + return nil, err + } + gasFeeCap := txmgr.CalcGasFeeCap(header.BaseFee, gasTipCap) + + // The estimated gas limits performed by RawTransact fail semi-regularly + // with out of gas exceptions. To remedy this we extract the internal calls + // to perform gas price/gas limit estimation here and add a buffer to + // account for any network variability. + gasLimit, err := d.cfg.L1Client.EstimateGas(ctx, ethereum.CallMsg{ + From: d.walletAddr, + To: &d.cfg.CTCAddr, + GasPrice: nil, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Value: nil, + Data: tx.Data(), + }) + if err != nil { + return nil, err + } + opts, err := bind.NewKeyedTransactorWithChainID( d.cfg.PrivKey, d.cfg.ChainID, ) @@ -275,28 +316,12 @@ func (d *Driver) UpdateGasPrice( } opts.Context = ctx opts.Nonce = new(big.Int).SetUint64(tx.Nonce()) + opts.GasTipCap = gasTipCap + opts.GasFeeCap = gasFeeCap + opts.GasLimit = 6 * gasLimit / 5 // add 20% buffer to gas limit opts.NoSend = true - finalTx, err := d.rawCtcContract.RawTransact(opts, tx.Data()) - switch { - case err == nil: - return finalTx, nil - - // If the transaction failed because the backend does not support - // eth_maxPriorityFeePerGas, fallback to using the default constant. - // Currently Alchemy is the only backend provider that exposes this method, - // so in the event their API is unreachable we can fallback to a degraded - // mode of operation. This also applies to our test environments, as hardhat - // doesn't support the query either. - case drivers.IsMaxPriorityFeePerGasNotFoundError(err): - log.Warn(d.cfg.Name + " eth_maxPriorityFeePerGas is unsupported " + - "by current backend, using fallback gasTipCap") - opts.GasTipCap = drivers.FallbackGasTipCap - return d.rawCtcContract.RawTransact(opts, tx.Data()) - - default: - return nil, err - } + return d.rawCtcContract.RawTransact(opts, tx.Data()) } // SendTransaction injects a signed transaction into the pending pool for