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( ...@@ -53,7 +53,7 @@ func ClearPendingTx(
log.Info(name+" clearing pending tx", "nonce", nonce) log.Info(name+" clearing pending tx", "nonce", nonce)
signedTx, err := SignClearingTx( signedTx, err := SignClearingTx(
ctx, walletAddr, nonce, l1Client, privKey, chainID, name, ctx, walletAddr, nonce, l1Client, privKey, chainID,
) )
if err != nil { if err != nil {
log.Error(name+" unable to sign clearing tx", "nonce", nonce, log.Error(name+" unable to sign clearing tx", "nonce", nonce,
...@@ -127,6 +127,7 @@ func ClearPendingTx( ...@@ -127,6 +127,7 @@ func ClearPendingTx(
// SignClearingTx creates a signed clearing tranaction which sends 0 ETH back to // 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. // the sender's address. EstimateGas is used to set an appropriate gas limit.
func SignClearingTx( func SignClearingTx(
name string,
ctx context.Context, ctx context.Context,
walletAddr common.Address, walletAddr common.Address,
nonce uint64, nonce uint64,
...@@ -137,7 +138,19 @@ func SignClearingTx( ...@@ -137,7 +138,19 @@ func SignClearingTx(
gasTipCap, err := l1Client.SuggestGasTipCap(ctx) gasTipCap, err := l1Client.SuggestGasTipCap(ctx)
if err != nil { if err != nil {
return nil, err 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) head, err := l1Client.HeaderByNumber(ctx, nil)
......
...@@ -73,8 +73,8 @@ func TestSignClearingTxEstimateGasSuccess(t *testing.T) { ...@@ -73,8 +73,8 @@ func TestSignClearingTxEstimateGasSuccess(t *testing.T) {
) )
tx, err := drivers.SignClearingTx( tx, err := drivers.SignClearingTx(
context.Background(), testWalletAddr, testNonce, l1Client, testPrivKey, "TEST", context.Background(), testWalletAddr, testNonce, l1Client,
testChainID, testPrivKey, testChainID,
) )
require.Nil(t, err) require.Nil(t, err)
require.NotNil(t, tx) require.NotNil(t, tx)
...@@ -103,8 +103,8 @@ func TestSignClearingTxSuggestGasTipCapFail(t *testing.T) { ...@@ -103,8 +103,8 @@ func TestSignClearingTxSuggestGasTipCapFail(t *testing.T) {
}) })
tx, err := drivers.SignClearingTx( tx, err := drivers.SignClearingTx(
context.Background(), testWalletAddr, testNonce, l1Client, testPrivKey, "TEST", context.Background(), testWalletAddr, testNonce, l1Client,
testChainID, testPrivKey, testChainID,
) )
require.Equal(t, errSuggestGasTipCap, err) require.Equal(t, errSuggestGasTipCap, err)
require.Nil(t, tx) require.Nil(t, tx)
...@@ -125,8 +125,8 @@ func TestSignClearingTxHeaderByNumberFail(t *testing.T) { ...@@ -125,8 +125,8 @@ func TestSignClearingTxHeaderByNumberFail(t *testing.T) {
}) })
tx, err := drivers.SignClearingTx( tx, err := drivers.SignClearingTx(
context.Background(), testWalletAddr, testNonce, l1Client, testPrivKey, "TEST", context.Background(), testWalletAddr, testNonce, l1Client,
testChainID, testPrivKey, testChainID,
) )
require.Equal(t, errHeaderByNumber, err) require.Equal(t, errHeaderByNumber, err)
require.Nil(t, tx) 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( ...@@ -201,7 +201,30 @@ func (d *Driver) CraftBatchTx(
blockOffset := new(big.Int).SetUint64(d.cfg.BlockOffset) blockOffset := new(big.Int).SetUint64(d.cfg.BlockOffset)
offsetStartsAtIndex := new(big.Int).Sub(start, 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 // SubmitBatchTx using the passed transaction as a template, signs and
...@@ -221,5 +244,24 @@ func (d *Driver) SubmitBatchTx( ...@@ -221,5 +244,24 @@ func (d *Driver) SubmitBatchTx(
opts.Context = ctx opts.Context = ctx
opts.Nonce = new(big.Int).SetUint64(tx.Nonce()) opts.Nonce = new(big.Int).SetUint64(tx.Nonce())
return d.rawSccContract.RawTransact(opts, tx.Data()) 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( ...@@ -234,7 +234,26 @@ func (d *Driver) CraftBatchTx(
opts.Nonce = nonce opts.Nonce = nonce
opts.NoSend = true opts.NoSend = true
return d.rawCtcContract.RawTransact(opts, batchCallData) 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( ...@@ -255,5 +274,24 @@ func (d *Driver) SubmitBatchTx(
opts.Context = ctx opts.Context = ctx
opts.Nonce = new(big.Int).SetUint64(tx.Nonce()) opts.Nonce = new(big.Int).SetUint64(tx.Nonce())
return d.rawCtcContract.RawTransact(opts, tx.Data()) 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