diff --git a/ethergo/chain/gas/cmp.go b/ethergo/chain/gas/cmp.go index 2ca13ef56d..30bf20eccd 100644 --- a/ethergo/chain/gas/cmp.go +++ b/ethergo/chain/gas/cmp.go @@ -106,6 +106,9 @@ func bumpDynamicTxFees(opts *bind.TransactOpts, percentIncrease int, baseFee, ma // BumpByPercent bumps a gas price by a percentage. func BumpByPercent(gasPrice *big.Int, percentIncrease int) *big.Int { + if gasPrice == nil { + return nil + } price := core.CopyBigInt(gasPrice) calculatedGasPrice := big.NewFloat(0).Mul(big.NewFloat(1+0.01*float64(percentIncrease)), big.NewFloat(0).SetInt(price)) price, _ = calculatedGasPrice.Int(price) diff --git a/ethergo/submitter/config/config.go b/ethergo/submitter/config/config.go index 1f1fd4c12e..cbb4f91de7 100644 --- a/ethergo/submitter/config/config.go +++ b/ethergo/submitter/config/config.go @@ -31,8 +31,8 @@ type ChainConfig struct { DoNotBatch bool `yaml:"skip_batching"` // MaxGasPrice is the maximum gas price to use for transactions MaxGasPrice *big.Int `yaml:"max_gas_price"` - // BaseGasPrice is the gas price that will be used if 0 is returned from the gas price oracle - BaseGasPrice *big.Int `yaml:"base_gas_price"` + // MinGasPrice is the gas price that will be used if 0 is returned from the gas price oracle + MinGasPrice *big.Int `yaml:"min_gas_price"` // BumpIntervalSeconds is the number of seconds to wait before bumping a transaction BumpIntervalSeconds int `yaml:"bump_interval_seconds"` // GasBumpPercentages is the percentage to bump the gas price by @@ -67,8 +67,8 @@ const ( // DefaultMaxPrice is the default max price of a tx. var DefaultMaxPrice = big.NewInt(500 * params.GWei) -// DefaultBaseGasPrice is the default max price of a tx. -var DefaultBaseGasPrice = big.NewInt(1 * params.GWei) +// DefaultMinGasPrice is the default min price of a tx. +var DefaultMinGasPrice = big.NewInt(1 * params.GWei) // note: there's probably a way to clean these getters up with generics, the real problem comes with the fact that // that this would require the caller to override the entire struct, which is not ideal.. @@ -110,17 +110,17 @@ func (c *Config) GetMaxGasPrice(chainID int) (maxPrice *big.Int) { return } -// GetBaseGasPrice returns the maximum gas price to use for transactions. -func (c *Config) GetBaseGasPrice(chainID int) (basePrice *big.Int) { - basePrice = c.BaseGasPrice +// GetMinGasPrice returns the minimum gas price to use for transactions. +func (c *Config) GetMinGasPrice(chainID int) (minPrice *big.Int) { + minPrice = c.MinGasPrice chainConfig, ok := c.Chains[chainID] - if ok && chainConfig.BaseGasPrice != nil { - basePrice = chainConfig.BaseGasPrice + if ok && chainConfig.MinGasPrice != nil { + minPrice = chainConfig.MinGasPrice } - if basePrice == nil || basePrice == big.NewInt(0) { - basePrice = DefaultBaseGasPrice + if minPrice == nil || minPrice == big.NewInt(0) { + minPrice = DefaultMinGasPrice } return } @@ -217,9 +217,9 @@ func (c *Config) SetGlobalMaxGasPrice(maxPrice *big.Int) { c.MaxGasPrice = maxPrice } -// SetBaseGasPrice is a helper function that sets the base gas price. -func (c *Config) SetBaseGasPrice(basePrice *big.Int) { - c.BaseGasPrice = basePrice +// SetMinGasPrice is a helper function that sets the base gas price. +func (c *Config) SetMinGasPrice(basePrice *big.Int) { + c.MinGasPrice = basePrice } // SetGlobalEIP1559Support is a helper function that sets the global EIP1559 support. diff --git a/ethergo/submitter/config/iconfig_generated.go b/ethergo/submitter/config/iconfig_generated.go index b6aac67620..86292a74e1 100644 --- a/ethergo/submitter/config/iconfig_generated.go +++ b/ethergo/submitter/config/iconfig_generated.go @@ -15,8 +15,8 @@ type IConfig interface { GetBatch(chainID int) bool // GetMaxGasPrice returns the maximum gas price to use for transactions. GetMaxGasPrice(chainID int) (maxPrice *big.Int) - // GetBaseGasPrice returns the maximum gas price to use for transactions. - GetBaseGasPrice(chainID int) (basePrice *big.Int) + // GetMinGasPrice returns the maximum gas price to use for transactions. + GetMinGasPrice(chainID int) (basePrice *big.Int) // GetBumpInterval returns the number of seconds to wait before bumping a transaction // TODO: test this method. GetBumpInterval(chainID int) time.Duration @@ -35,8 +35,8 @@ type IConfig interface { SupportsEIP1559(chainID int) bool // SetGlobalMaxGasPrice is a helper function that sets the global gas price. SetGlobalMaxGasPrice(maxPrice *big.Int) - // SetBaseGasPrice is a helper function that sets the base gas price. - SetBaseGasPrice(basePrice *big.Int) + // SetMinGasPrice is a helper function that sets the base gas price. + SetMinGasPrice(basePrice *big.Int) // SetGlobalEIP1559Support is a helper function that sets the global EIP1559 support. SetGlobalEIP1559Support(supportsEIP1559 bool) } diff --git a/ethergo/submitter/submitter.go b/ethergo/submitter/submitter.go index a9f35ba0b4..a919b3a6dd 100644 --- a/ethergo/submitter/submitter.go +++ b/ethergo/submitter/submitter.go @@ -17,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/ipfs/go-log" "github.com/synapsecns/sanguine/core" "github.com/synapsecns/sanguine/core/mapmutex" @@ -346,119 +347,194 @@ func (t *txSubmitterImpl) txTypeForChain(chainID *big.Int) (txType uint8) { } // setGasPrice sets the gas price for the transaction. -// it bumps if prevtx is set -// nolint: cyclop -// TODO: use options. +// If a prevTx is specified, a bump will be attempted; otherwise values will be +// set from the gas oracle. +// If gas values exceed the configured max, an error will be returned. func (t *txSubmitterImpl) setGasPrice(ctx context.Context, client client.EVM, transactor *bind.TransactOpts, bigChainID *big.Int, prevTx *types.Transaction) (err error) { ctx, span := t.metrics.Tracer().Start(ctx, "submitter.setGasPrice") chainID := int(bigChainID.Uint64()) - maxPrice := t.config.GetMaxGasPrice(chainID) + useDynamic := t.config.SupportsEIP1559(chainID) defer func() { - if transactor.GasPrice != nil && maxPrice.Cmp(transactor.GasPrice) < 0 { - transactor.GasPrice = maxPrice + span.SetAttributes( + attribute.Int(metrics.ChainID, chainID), + attribute.Bool("use_dynamic", useDynamic), + attribute.String("gas_price", bigPtrToString(transactor.GasPrice)), + attribute.String("gas_fee_cap", bigPtrToString(transactor.GasFeeCap)), + attribute.String("gas_tip_cap", bigPtrToString(transactor.GasTipCap)), + ) + metrics.EndSpanWithErr(span, err) + }() + + t.bumpGasFromPrevTx(ctx, transactor, prevTx, chainID, useDynamic) + t.applyGasFloor(ctx, transactor, chainID, useDynamic) + + err = t.applyGasFromOracle(ctx, transactor, client, useDynamic) + if err != nil { + return fmt.Errorf("could not populate gas from oracle: %w", err) + } + + err = t.applyGasCeil(ctx, transactor, chainID, useDynamic) + if err != nil { + return fmt.Errorf("could not apply gas ceil: %w", err) + } + return nil +} + +// bumpGasFromPrevTx populates the gas fields from the previous transaction and bumps +// the appropriate values corresponding to the configured GasBumpPercentage. +// Note that in the event of a tx type mismatch, gasFeeCap is copied to gasPrice, +// and gasPrice is copied to both gasFeeCap and gasTipCap in the opposite scenario. +// +//nolint:nestif +func (t *txSubmitterImpl) bumpGasFromPrevTx(ctx context.Context, transactor *bind.TransactOpts, prevTx *types.Transaction, chainID int, currentDynamic bool) { + if prevTx == nil { + return + } + + _, span := t.metrics.Tracer().Start(ctx, "submitter.bumpGasFromPrevTx") + + defer func() { + span.SetAttributes( + attribute.String("gas_price", bigPtrToString(transactor.GasPrice)), + attribute.String("gas_fee_cap", bigPtrToString(transactor.GasFeeCap)), + attribute.String("gas_tip_cap", bigPtrToString(transactor.GasTipCap)), + ) + metrics.EndSpan(span) + }() + + prevDynamic := prevTx.Type() == types.DynamicFeeTxType + bumpPct := t.config.GetGasBumpPercentage(chainID) + if currentDynamic { + if prevDynamic { + transactor.GasFeeCap = gas.BumpByPercent(core.CopyBigInt(prevTx.GasFeeCap()), bumpPct) + transactor.GasTipCap = gas.BumpByPercent(core.CopyBigInt(prevTx.GasTipCap()), bumpPct) + } else { + transactor.GasFeeCap = gas.BumpByPercent(core.CopyBigInt(prevTx.GasPrice()), bumpPct) + transactor.GasTipCap = gas.BumpByPercent(core.CopyBigInt(prevTx.GasPrice()), bumpPct) } - if transactor.GasFeeCap != nil && maxPrice.Cmp(transactor.GasFeeCap) < 0 { - transactor.GasFeeCap = maxPrice + } else { + if prevDynamic { + transactor.GasPrice = gas.BumpByPercent(core.CopyBigInt(prevTx.GasFeeCap()), bumpPct) + } else { + transactor.GasPrice = gas.BumpByPercent(core.CopyBigInt(prevTx.GasPrice()), bumpPct) } + } +} + +var minTipCap = big.NewInt(10 * params.Wei) +// applyGasFloor applies the min gas price from the config if values are unset. +// +//nolint:cyclop,nestif +func (t *txSubmitterImpl) applyGasFloor(ctx context.Context, transactor *bind.TransactOpts, chainID int, useDynamic bool) { + _, span := t.metrics.Tracer().Start(ctx, "submitter.applyGasFloor") + + defer func() { span.SetAttributes( attribute.String("gas_price", bigPtrToString(transactor.GasPrice)), attribute.String("gas_fee_cap", bigPtrToString(transactor.GasFeeCap)), attribute.String("gas_tip_cap", bigPtrToString(transactor.GasTipCap)), ) - metrics.EndSpanWithErr(span, err) + metrics.EndSpan(span) }() - // TODO: cache both of these values - shouldBump := true - useEIP1559 := t.config.SupportsEIP1559(chainID) - if useEIP1559 { - transactor.GasFeeCap, err = client.SuggestGasPrice(ctx) - if err != nil { - return fmt.Errorf("could not get gas price: %w", err) + gasFloor := t.config.GetMinGasPrice(chainID) + if useDynamic { + if transactor.GasFeeCap == nil || transactor.GasFeeCap.Cmp(gasFloor) < 0 { + transactor.GasFeeCap = gasFloor } - // don't bump fee cap if we hit the max configured gas price - if transactor.GasFeeCap.Cmp(maxPrice) > 0 { - transactor.GasFeeCap = maxPrice - shouldBump = false - span.AddEvent("not bumping fee cap since max price is reached") + if transactor.GasTipCap == nil || transactor.GasTipCap.Cmp(gasFloor) < 0 { + transactor.GasTipCap = minTipCap } + } else if transactor.GasPrice == nil || transactor.GasPrice.Cmp(gasFloor) < 0 { + transactor.GasPrice = gasFloor + } +} + +// applyGasFromOracle fetches gas values from a RPC endpoint and attempts to set them. +// If values are already specified, they will be overridden if the oracle values are higher. +func (t *txSubmitterImpl) applyGasFromOracle(ctx context.Context, transactor *bind.TransactOpts, client client.EVM, useDynamic bool) (err error) { + ctx, span := t.metrics.Tracer().Start(ctx, "submitter.applyGasFromOracle") - transactor.GasTipCap, err = client.SuggestGasTipCap(ctx) + defer func() { + metrics.EndSpanWithErr(span, err) + }() + + if useDynamic { + suggestedGasFeeCap, err := client.SuggestGasPrice(ctx) + if err != nil { + return fmt.Errorf("could not get gas fee cap: %w", err) + } + transactor.GasFeeCap = maxOfBig(transactor.GasFeeCap, suggestedGasFeeCap) + suggestedGasTipCap, err := client.SuggestGasTipCap(ctx) if err != nil { return fmt.Errorf("could not get gas tip cap: %w", err) } + transactor.GasTipCap = maxOfBig(transactor.GasTipCap, suggestedGasTipCap) + span.SetAttributes( + attribute.String("suggested_gas_fee_cap", bigPtrToString(suggestedGasFeeCap)), + attribute.String("suggested_gas_tip_cap", bigPtrToString(suggestedGasTipCap)), + attribute.String("gas_fee_cap", bigPtrToString(transactor.GasFeeCap)), + attribute.String("gas_tip_cap", bigPtrToString(transactor.GasTipCap)), + ) } else { - transactor.GasPrice, err = client.SuggestGasPrice(ctx) + suggestedGasPrice, err := client.SuggestGasPrice(ctx) if err != nil { return fmt.Errorf("could not get gas price: %w", err) } + transactor.GasPrice = maxOfBig(transactor.GasPrice, suggestedGasPrice) + span.SetAttributes( + attribute.String("suggested_gas_price", bigPtrToString(suggestedGasPrice)), + attribute.String("gas_price", bigPtrToString(transactor.GasPrice)), + ) } - t.applyBaseGasPrice(transactor, chainID) + return nil +} - //nolint: nestif - if prevTx != nil && shouldBump { - gasBlock, err := t.getGasBlock(ctx, client, chainID) - if err != nil { - span.AddEvent("could not get gas block", trace.WithAttributes(attribute.String("error", err.Error()))) - return err - } +// applyGasCeil evaluates current gas values versus the configured maximum, and +// returns an error if they exceed the maximum. +func (t *txSubmitterImpl) applyGasCeil(ctx context.Context, transactor *bind.TransactOpts, chainID int, useDynamic bool) (err error) { + _, span := t.metrics.Tracer().Start(ctx, "submitter.applyGasCeil") - // if the prev tx was greater than this one, we should bump the gas price from that point - if prevTx.Type() == types.LegacyTxType { - if prevTx.GasPrice().Cmp(transactor.GasPrice) > 0 { - transactor.GasPrice = core.CopyBigInt(prevTx.GasPrice()) - } - } else { - if prevTx.GasTipCap().Cmp(transactor.GasTipCap) > 0 { - transactor.GasTipCap = core.CopyBigInt(prevTx.GasTipCap()) - } + maxPrice := t.config.GetMaxGasPrice(chainID) - if prevTx.GasFeeCap().Cmp(transactor.GasFeeCap) > 0 { - transactor.GasFeeCap = core.CopyBigInt(prevTx.GasFeeCap()) - } + defer func() { + span.SetAttributes(attribute.String("max_price", bigPtrToString(maxPrice))) + metrics.EndSpanWithErr(span, err) + }() + + if useDynamic { + if transactor.GasFeeCap.Cmp(maxPrice) > 0 { + return fmt.Errorf("gas fee cap %s exceeds max price %s", transactor.GasFeeCap, maxPrice) + } + if transactor.GasTipCap.Cmp(transactor.GasFeeCap) > 0 { + transactor.GasTipCap = core.CopyBigInt(transactor.GasFeeCap) + span.AddEvent("tip cap exceeds fee cap; setting tip cap to fee cap") } - gas.BumpGasFees(transactor, t.config.GetGasBumpPercentage(chainID), gasBlock.BaseFee, maxPrice) } else { - // if we're not bumping, we should still make sure the gas price is at least 10 because 10% of 10 wei is a whole number. - // TODO: this should be customizable. - if useEIP1559 { - transactor.GasTipCap = maxOfBig(transactor.GasTipCap, big.NewInt(10)) - } else { - transactor.GasPrice = maxOfBig(transactor.GasPrice, big.NewInt(10)) + if transactor.GasPrice.Cmp(maxPrice) > 0 { + return fmt.Errorf("gas price %s exceeds max price %s", transactor.GasPrice, maxPrice) } } return nil } -// b must not be nil. func maxOfBig(a, b *big.Int) *big.Int { if a == nil { return b } + if b == nil { + return a + } if a.Cmp(b) > 0 { return a } return b } -// applyBaseGasPrice applies the base gas price to the transactor if a gas price value is zero. -func (t *txSubmitterImpl) applyBaseGasPrice(transactor *bind.TransactOpts, chainID int) { - if t.config.SupportsEIP1559(chainID) { - if transactor.GasFeeCap == nil || transactor.GasFeeCap.Cmp(big.NewInt(0)) == 0 { - transactor.GasFeeCap = t.config.GetBaseGasPrice(chainID) - } - // TODO: we need to keep gas tip cap non-zero, but below the base gas price. - } else { - if transactor.GasPrice == nil || transactor.GasPrice.Cmp(big.NewInt(0)) == 0 { - transactor.GasPrice = t.config.GetBaseGasPrice(chainID) - } - } -} - // getGasBlock gets the gas block for the given chain. func (t *txSubmitterImpl) getGasBlock(ctx context.Context, chainClient client.EVM, chainID int) (gasBlock *types.Header, err error) { ctx, span := t.metrics.Tracer().Start(ctx, "submitter.getGasBlock") diff --git a/ethergo/submitter/submitter_test.go b/ethergo/submitter/submitter_test.go index b716165038..b95578bc11 100644 --- a/ethergo/submitter/submitter_test.go +++ b/ethergo/submitter/submitter_test.go @@ -4,9 +4,9 @@ import ( "fmt" "math/big" - "github.com/brianvoe/gofakeit/v6" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/mock" "github.com/synapsecns/sanguine/core/testsuite" clientMocks "github.com/synapsecns/sanguine/ethergo/client/mocks" @@ -30,69 +30,179 @@ func (s *SubmitterSuite) TestSetGasPrice() { signer := localsigner.NewSigner(wall.PrivateKey()) - chainID := s.testBackends[0].GetBigChainID() + legacyChainID := s.testBackends[0].GetBigChainID() + dynamicChainID := s.testBackends[1].GetBigChainID() + client := new(clientMocks.EVM) + legacyTransactor, err := signer.GetTransactor(s.GetTestContext(), legacyChainID) + s.Require().NoError(err) - transactor, err := signer.GetTransactor(s.GetTestContext(), chainID) + dynamicTransactor, err := signer.GetTransactor(s.GetTestContext(), dynamicChainID) s.Require().NoError(err) - cfg := &config.Config{} + maxGasPrice := big.NewInt(1000 * params.GWei) + minGasPrice := big.NewInt(1 * params.GWei) + cfg := &config.Config{ + Chains: map[int]config.ChainConfig{ + int(legacyChainID.Int64()): { + MinGasPrice: minGasPrice, + MaxGasPrice: maxGasPrice, + SupportsEIP1559: false, + }, + int(dynamicChainID.Int64()): { + MinGasPrice: minGasPrice, + MaxGasPrice: maxGasPrice, + SupportsEIP1559: true, + }, + }, + } ts := submitter.NewTestTransactionSubmitter(s.metrics, signer, s, s.store, cfg) - // 1. Test with gas price set, but not one that exceeds max (not eip-1559) - gasPrice := new(big.Int).SetUint64(gofakeit.Uint64()) - maxPrice := new(big.Int).Add(gasPrice, new(big.Int).SetUint64(1)) - cfg.SetGlobalMaxGasPrice(maxPrice) + resetTransactors := func() { + legacyTransactor.GasPrice = nil + dynamicTransactor.GasFeeCap = nil + dynamicTransactor.GasTipCap = nil + } - client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Times(3).Return(gasPrice, nil) - err = ts.SetGasPrice(s.GetTestContext(), client, transactor, chainID, nil) - s.Require().NoError(err) + getLegacyTx := func(gasPrice *big.Int) *types.Transaction { + return types.NewTx(&types.LegacyTx{ + GasPrice: gasPrice, + }) + } - s.Equal(gasPrice, transactor.GasPrice, testsuite.BigIntComparer()) + getDynamicTx := func(gasFeeCap, gasTipCap *big.Int) *types.Transaction { + return types.NewTx(&types.DynamicFeeTx{ + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + }) + } - // 2. Test with gas price set, but one that exceeds max, should return max (not eip-1559) - maxPrice = new(big.Int).Sub(gasPrice, new(big.Int).SetUint64(1)) - cfg.SetGlobalMaxGasPrice(maxPrice) + assertGasValues := func(transactor *bind.TransactOpts, gasPrice, gasFeeCap, gasTipCap *big.Int) { + s.Equal(gasPrice, transactor.GasPrice, testsuite.BigIntComparer()) + s.Equal(gasFeeCap, transactor.GasFeeCap, testsuite.BigIntComparer()) + s.Equal(gasTipCap, transactor.GasTipCap, testsuite.BigIntComparer()) + } - err = ts.SetGasPrice(s.GetTestContext(), client, transactor, chainID, nil) - s.Require().NoError(err) - s.Equal(maxPrice, transactor.GasPrice, testsuite.BigIntComparer()) + s.Run("LegacyTx:FromOracle", func() { + resetTransactors() + gasPrice := big.NewInt(100 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, legacyTransactor, legacyChainID, nil) + s.Require().NoError(err) + assertGasValues(legacyTransactor, gasPrice, nil, nil) + }) - // 3. Test with gas price set, but one that exceeds max, should return max (legacy tx) - cfg.SetGlobalEIP1559Support(true) - tipCap := new(big.Int).SetUint64(uint64(gofakeit.Uint32())) - client.On(testsuite.GetFunctionName(client.SuggestGasTipCap), mock.Anything).Once().Return(tipCap, nil) + s.Run("DynamicTx:FromOracle", func() { + resetTransactors() + gasPrice := big.NewInt(120 * params.GWei) + gasTipCap := big.NewInt(50 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + client.On(testsuite.GetFunctionName(client.SuggestGasTipCap), mock.Anything).Once().Return(gasTipCap, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, dynamicTransactor, dynamicChainID, nil) + s.Require().NoError(err) + assertGasValues(dynamicTransactor, nil, gasPrice, gasTipCap) + }) - err = ts.SetGasPrice(s.GetTestContext(), client, transactor, chainID, nil) - s.Require().NoError(err) + s.Run("LegacyTx:BelowMin", func() { + resetTransactors() + gasPrice := big.NewInt(1 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, legacyTransactor, legacyChainID, nil) + s.Require().NoError(err) + assertGasValues(legacyTransactor, minGasPrice, nil, nil) + }) - s.Equal(tipCap, transactor.GasTipCap, testsuite.BigIntComparer()) - s.Equal(maxPrice, transactor.GasFeeCap, testsuite.BigIntComparer()) + s.Run("DynamicTx:BelowMin", func() { + resetTransactors() + gasPrice := big.NewInt(0.5 * params.GWei) + gasTipCap := big.NewInt(0) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + client.On(testsuite.GetFunctionName(client.SuggestGasTipCap), mock.Anything).Once().Return(gasTipCap, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, dynamicTransactor, dynamicChainID, nil) + s.Require().NoError(err) + assertGasValues(dynamicTransactor, nil, minGasPrice, big.NewInt(10*params.Wei)) + }) - // 4. Test with zero gas price, should return base gas price - cfg.SetGlobalEIP1559Support(false) - baseGasPrice := new(big.Int).SetUint64(uint64(gofakeit.Uint32())) - cfg.SetBaseGasPrice(baseGasPrice) - gasPrice = big.NewInt(0) - client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Return(gasPrice, nil) + s.Run("LegacyTx:AboveMax", func() { + resetTransactors() + gasPrice := big.NewInt(10000 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, legacyTransactor, legacyChainID, nil) + s.NotNil(err) + }) - err = ts.SetGasPrice(s.GetTestContext(), client, transactor, chainID, nil) - s.Require().NoError(err) + s.Run("DynamicTx:AboveMax", func() { + resetTransactors() + gasPrice := big.NewInt(20000 * params.GWei) + gasTipCap := big.NewInt(10000 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + client.On(testsuite.GetFunctionName(client.SuggestGasTipCap), mock.Anything).Once().Return(gasTipCap, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, dynamicTransactor, dynamicChainID, nil) + s.NotNil(err) + }) - s.Equal(baseGasPrice, transactor.GasPrice, testsuite.BigIntComparer()) + s.Run("LegacyTx:SimpleBump", func() { + resetTransactors() + prevTx := getLegacyTx(big.NewInt(100 * params.GWei)) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(big.NewInt(50*params.GWei), nil) + err = ts.SetGasPrice(s.GetTestContext(), client, legacyTransactor, legacyChainID, prevTx) + s.Require().NoError(err) + assertGasValues(legacyTransactor, big.NewInt(110*params.GWei), nil, nil) + }) - // 5. Test with zero gas price with EIP1559, should return base gas price - cfg.SetGlobalEIP1559Support(true) - gasPrice = big.NewInt(0) - client.On(testsuite.GetFunctionName(client.SuggestGasTipCap), mock.Anything).Return(gasPrice, nil) + s.Run("DynamicTx:SimpleBump", func() { + resetTransactors() + prevTx := getDynamicTx(big.NewInt(150*params.GWei), big.NewInt(110*params.GWei)) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(big.NewInt(70*params.GWei), nil) + client.On(testsuite.GetFunctionName(client.SuggestGasTipCap), mock.Anything).Once().Return(big.NewInt(60*params.GWei), nil) + err = ts.SetGasPrice(s.GetTestContext(), client, dynamicTransactor, dynamicChainID, prevTx) + s.Require().NoError(err) + assertGasValues(dynamicTransactor, nil, big.NewInt(165*params.GWei), big.NewInt(121*params.GWei)) + }) - err = ts.SetGasPrice(s.GetTestContext(), client, transactor, chainID, nil) - s.Require().NoError(err) + s.Run("LegacyTx:BumpWithOracleOverride", func() { + resetTransactors() + prevTx := getLegacyTx(big.NewInt(100 * params.GWei)) + gasPrice := big.NewInt(200 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, legacyTransactor, legacyChainID, prevTx) + s.Require().NoError(err) + assertGasValues(legacyTransactor, gasPrice, nil, nil) + }) - s.Equal(baseGasPrice, transactor.GasTipCap, testsuite.BigIntComparer()) + s.Run("DynamicTx:BumpWithOracleOverride", func() { + resetTransactors() + prevTx := getDynamicTx(big.NewInt(150*params.GWei), big.NewInt(110*params.GWei)) + gasPrice := big.NewInt(200 * params.GWei) + gasTipCap := big.NewInt(150 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + client.On(testsuite.GetFunctionName(client.SuggestGasTipCap), mock.Anything).Once().Return(gasTipCap, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, dynamicTransactor, dynamicChainID, prevTx) + s.Require().NoError(err) + assertGasValues(dynamicTransactor, nil, gasPrice, gasTipCap) + }) - // 6. Test with bump (TODO) - // 7. Test with bump and max (TODO) + s.Run("LegacyTx:BumpWithPrevDynamicTx", func() { + resetTransactors() + prevTx := getDynamicTx(big.NewInt(100*params.GWei), big.NewInt(80*params.GWei)) + gasPrice := big.NewInt(50 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, legacyTransactor, legacyChainID, prevTx) + s.Require().NoError(err) + assertGasValues(legacyTransactor, big.NewInt(110*params.GWei), nil, nil) + }) + + s.Run("DynamicTx:BumpWithPrevLegacyTx", func() { + resetTransactors() + prevTx := getLegacyTx(big.NewInt(100 * params.GWei)) + gasPrice := big.NewInt(50 * params.GWei) + gasTipCap := big.NewInt(25 * params.GWei) + client.On(testsuite.GetFunctionName(client.SuggestGasPrice), mock.Anything).Once().Return(gasPrice, nil) + client.On(testsuite.GetFunctionName(client.SuggestGasTipCap), mock.Anything).Once().Return(gasTipCap, nil) + err = ts.SetGasPrice(s.GetTestContext(), client, dynamicTransactor, dynamicChainID, prevTx) + s.Require().NoError(err) + assertGasValues(dynamicTransactor, nil, big.NewInt(110*params.GWei), big.NewInt(110*params.GWei)) + }) } func (s *SubmitterSuite) TestGetGasBlock() {