Commit 53a8b149 authored by Conner Fromknecht's avatar Conner Fromknecht

feat: add SuggestGasTipCap fallback

This commit adds a fallback in case the L1 provider does not support
queries for eth_maxPriorityFeePerGas. Of our infrastructure providers,
i.e. Alchemy, Infura, Quicknode, and Hardhat (in test envs), only
Alchemy suports this call. In production, we will be routing this call
directly to Alchemy, which should work in the general case. However, if
they were to be unavailable the batch submitter would be stalled until
service was restored. This modification also allows the new EIP-1559
batch submitter to pass our integration tests, since the default Hardhat
backend can't support the query either.
parent c1eba2e6
......@@ -53,7 +53,7 @@ func ClearPendingTx(
log.Info(name+" clearing pending tx", "nonce", nonce)
signedTx, err := SignClearingTx(
ctx, walletAddr, nonce, l1Client, privKey, chainID,
name, ctx, walletAddr, nonce, l1Client, privKey, chainID,
)
if err != nil {
log.Error(name+" unable to sign clearing tx", "nonce", nonce,
......@@ -127,6 +127,7 @@ func ClearPendingTx(
// SignClearingTx creates a signed clearing tranaction which sends 0 ETH back to
// the sender's address. EstimateGas is used to set an appropriate gas limit.
func SignClearingTx(
name string,
ctx context.Context,
walletAddr common.Address,
nonce uint64,
......@@ -137,9 +138,21 @@ func SignClearingTx(
gasTipCap, err := l1Client.SuggestGasTipCap(ctx)
if err != nil {
if !IsMaxPriorityFeePerGasNotFoundError(err) {
return nil, err
}
// 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.
log.Warn(name + " eth_maxPriorityFeePerGas is unsupported " +
"by current backend, using fallback gasTipCap")
gasTipCap = FallbackGasTipCap
}
head, err := l1Client.HeaderByNumber(ctx, nil)
if err != nil {
return nil, err
......
......@@ -73,8 +73,8 @@ func TestSignClearingTxEstimateGasSuccess(t *testing.T) {
)
tx, err := drivers.SignClearingTx(
context.Background(), testWalletAddr, testNonce, l1Client, testPrivKey,
testChainID,
"TEST", context.Background(), testWalletAddr, testNonce, l1Client,
testPrivKey, testChainID,
)
require.Nil(t, err)
require.NotNil(t, tx)
......@@ -103,8 +103,8 @@ func TestSignClearingTxSuggestGasTipCapFail(t *testing.T) {
})
tx, err := drivers.SignClearingTx(
context.Background(), testWalletAddr, testNonce, l1Client, testPrivKey,
testChainID,
"TEST", context.Background(), testWalletAddr, testNonce, l1Client,
testPrivKey, testChainID,
)
require.Equal(t, errSuggestGasTipCap, err)
require.Nil(t, tx)
......@@ -125,8 +125,8 @@ func TestSignClearingTxHeaderByNumberFail(t *testing.T) {
})
tx, err := drivers.SignClearingTx(
context.Background(), testWalletAddr, testNonce, l1Client, testPrivKey,
testChainID,
"TEST", context.Background(), testWalletAddr, testNonce, l1Client,
testPrivKey, testChainID,
)
require.Equal(t, errHeaderByNumber, err)
require.Nil(t, tx)
......
package drivers
import (
"errors"
"math/big"
"strings"
)
var (
errMaxPriorityFeePerGasNotFound = errors.New(
"Method eth_maxPriorityFeePerGas not found",
)
// FallbackGasTipCap is the default fallback gasTipCap used when we are
// unable to query an L1 backend for a suggested gasTipCap.
FallbackGasTipCap = big.NewInt(1500000000)
)
// IsMaxPriorityFeePerGasNotFoundError returns true if the provided error
// signals that the backend does not support the eth_maxPrirorityFeePerGas
// method. In this case, the caller should fallback to using the constant above.
func IsMaxPriorityFeePerGasNotFoundError(err error) bool {
return strings.Contains(
err.Error(), errMaxPriorityFeePerGasNotFound.Error(),
)
}
......@@ -201,7 +201,30 @@ func (d *Driver) CraftBatchTx(
blockOffset := new(big.Int).SetUint64(d.cfg.BlockOffset)
offsetStartsAtIndex := new(big.Int).Sub(start, blockOffset)
return d.sccContract.AppendStateBatch(opts, stateRoots, offsetStartsAtIndex)
tx, err := d.sccContract.AppendStateBatch(
opts, stateRoots, offsetStartsAtIndex,
)
switch {
case err == nil:
return tx, 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.sccContract.AppendStateBatch(
opts, stateRoots, offsetStartsAtIndex,
)
default:
return nil, err
}
}
// SubmitBatchTx using the passed transaction as a template, signs and
......@@ -221,5 +244,24 @@ func (d *Driver) SubmitBatchTx(
opts.Context = ctx
opts.Nonce = new(big.Int).SetUint64(tx.Nonce())
finalTx, err := d.rawSccContract.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.rawSccContract.RawTransact(opts, tx.Data())
default:
return nil, err
}
}
......@@ -234,7 +234,26 @@ func (d *Driver) CraftBatchTx(
opts.Nonce = nonce
opts.NoSend = true
tx, err := d.rawCtcContract.RawTransact(opts, batchCallData)
switch {
case err == nil:
return tx, 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, batchCallData)
default:
return nil, err
}
}
}
......@@ -255,5 +274,24 @@ func (d *Driver) SubmitBatchTx(
opts.Context = ctx
opts.Nonce = new(big.Int).SetUint64(tx.Nonce())
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
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment