From 5edd3bad3d5789c19843d4aed725c940f048a587 Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Fri, 24 Feb 2023 14:19:55 -0700 Subject: [PATCH 01/10] combine EVM legacy + dynamic fee estimation behind a single interface --- core/chains/evm/gas/models.go | 75 ++++++++++++++++++++++-- core/chains/evm/txmgr/eth_broadcaster.go | 55 +++++++++-------- core/chains/evm/txmgr/eth_confirmer.go | 22 +++---- core/chains/evm/txmgr/txmgr.go | 8 +-- 4 files changed, 115 insertions(+), 45 deletions(-) diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index f650e5b2cae..eadd7797a64 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -28,7 +28,7 @@ func IsBumpErr(err error) bool { } // NewEstimator returns the estimator for a given config -func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) Estimator { +func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) FeeEstimator { s := cfg.GasEstimatorMode() lggr.Infow(fmt.Sprintf("Initializing EVM gas estimator in mode: %s", s), "estimatorMode", s, @@ -51,16 +51,16 @@ func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) Es ) switch s { case "Arbitrum": - return NewArbitrumEstimator(lggr, cfg, ethClient, ethClient) + return NewWrappedEvmEstimator(NewArbitrumEstimator(lggr, cfg, ethClient, ethClient), cfg) case "BlockHistory": - return NewBlockHistoryEstimator(lggr, ethClient, cfg, *ethClient.ChainID()) + return NewWrappedEvmEstimator(NewBlockHistoryEstimator(lggr, ethClient, cfg, *ethClient.ChainID()), cfg) case "FixedPrice": - return NewFixedPriceEstimator(cfg, lggr) + return NewWrappedEvmEstimator(NewFixedPriceEstimator(cfg, lggr), cfg) case "Optimism2", "L2Suggested": - return NewL2SuggestedPriceEstimator(lggr, ethClient) + return NewWrappedEvmEstimator(NewL2SuggestedPriceEstimator(lggr, ethClient), cfg) default: lggr.Warnf("GasEstimator: unrecognised mode '%s', falling back to FixedPriceEstimator", s) - return NewFixedPriceEstimator(cfg, lggr) + return NewWrappedEvmEstimator(NewFixedPriceEstimator(cfg, lggr), cfg) } } @@ -106,6 +106,69 @@ type Estimator interface { BumpDynamicFee(ctx context.Context, original DynamicFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) } +type EvmFee struct { + Legacy *assets.Wei + Dynamic *DynamicFee +} + +type FeeEstimator interface { + OnNewLongestChain(context.Context, *evmtypes.Head) + Start(context.Context) error + Close() error + + GetFee(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...Opt) (fee EvmFee, chainSpecificGasLimit uint32, err error) + BumpFee(ctx context.Context, originalFee EvmFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumpedFee EvmFee, chainSpecificGasLimit uint32, err error) +} + +type WrappedEvmEstimator struct { + Estimator + EIP1559Enabled bool +} + +var _ FeeEstimator = (*WrappedEvmEstimator)(nil) + +func NewWrappedEvmEstimator(e Estimator, cfg Config) FeeEstimator { + return &WrappedEvmEstimator{ + Estimator: e, + EIP1559Enabled: cfg.EvmEIP1559DynamicFees(), + } +} + +func (e WrappedEvmEstimator) GetFee(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...Opt) (fee EvmFee, chainSpecificGasLimit uint32, err error) { + // get dynamic fee + if e.EIP1559Enabled { + var dynamicFee DynamicFee + dynamicFee, chainSpecificGasLimit, err = e.Estimator.GetDynamicFee(ctx, gasLimit, maxGasPriceWei) + fee.Dynamic = &dynamicFee + return + } + + // get legacy fee + fee.Legacy, chainSpecificGasLimit, err = e.Estimator.GetLegacyGas(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + return +} + +func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumpedFee EvmFee, chainSpecificGasLimit uint32, err error) { + // validate only 1 fee type is present + if (originalFee.Dynamic == nil && originalFee.Legacy == nil) || (originalFee.Dynamic != nil && originalFee.Legacy != nil) { + err = errors.New("only one dynamic or legacy fee can be defined") + return + } + + // bump fee based on what fee the tx has previously used (not based on config) + // bump dynamic original + if originalFee.Dynamic != nil { + var bumpedDynamic DynamicFee + bumpedDynamic, chainSpecificGasLimit, err = e.Estimator.BumpDynamicFee(ctx, *originalFee.Dynamic, gasLimit, maxGasPriceWei, attempts) + bumpedFee.Dynamic = &bumpedDynamic + return + } + + // bump legacy fee + bumpedFee.Legacy, chainSpecificGasLimit, err = e.Estimator.BumpLegacyGas(ctx, originalFee.Legacy, gasLimit, maxGasPriceWei, attempts) + return +} + // Opt is an option for a gas estimator type Opt int diff --git a/core/chains/evm/txmgr/eth_broadcaster.go b/core/chains/evm/txmgr/eth_broadcaster.go index 67cc9a546bc..94140ede9b6 100644 --- a/core/chains/evm/txmgr/eth_broadcaster.go +++ b/core/chains/evm/txmgr/eth_broadcaster.go @@ -95,7 +95,7 @@ type EthBroadcaster struct { q pg.Q ethClient evmclient.Client ChainKeyStore - estimator gas.Estimator + estimator gas.FeeEstimator resumeCallback ResumeCallback // autoSyncNonce, if set, will cause EthBroadcaster to fast-forward the nonce @@ -123,7 +123,7 @@ type EthBroadcaster struct { // NewEthBroadcaster returns a new concrete EthBroadcaster func NewEthBroadcaster(db *sqlx.DB, ethClient evmclient.Client, config Config, keystore KeyStore, eventBroadcaster pg.EventBroadcaster, - keyStates []ethkey.State, estimator gas.Estimator, resumeCallback ResumeCallback, + keyStates []ethkey.State, estimator gas.FeeEstimator, resumeCallback ResumeCallback, logger logger.Logger, checkerFactory TransmitCheckerFactory, autoSyncNonce bool) *EthBroadcaster { triggers := make(map[gethCommon.Address]chan struct{}) @@ -385,21 +385,18 @@ func (eb *EthBroadcaster) processUnstartedEthTxs(ctx context.Context, fromAddres n++ var a EthTxAttempt keySpecificMaxGasPriceWei := eb.config.KeySpecificMaxGasPriceWei(etx.FromAddress) + fee, gasLimit, err := eb.estimator.GetFee(ctx, etx.EncodedPayload, etx.GasLimit, keySpecificMaxGasPriceWei) + if err != nil { + return errors.Wrap(err, "failed to get fee"), true + } + if eb.config.EvmEIP1559DynamicFees() { - fee, gasLimit, err := eb.estimator.GetDynamicFee(ctx, etx.GasLimit, keySpecificMaxGasPriceWei) - if err != nil { - return errors.Wrap(err, "failed to get dynamic gas fee"), true - } - a, err = eb.NewDynamicFeeAttempt(*etx, fee, gasLimit) + a, err = eb.NewDynamicFeeAttempt(*etx, *fee.Dynamic, gasLimit) if err != nil { return errors.Wrap(err, "processUnstartedEthTxs failed on NewDynamicFeeAttempt"), true } } else { - gasPrice, gasLimit, err := eb.estimator.GetLegacyGas(ctx, etx.EncodedPayload, etx.GasLimit, keySpecificMaxGasPriceWei) - if err != nil { - return errors.Wrap(err, "failed to estimate gas"), true - } - a, err = eb.NewLegacyAttempt(*etx, gasPrice, gasLimit) + a, err = eb.NewLegacyAttempt(*etx, fee.Legacy, gasLimit) if err != nil { return errors.Wrap(err, "processUnstartedEthTxs failed on NewLegacyAttempt"), true } @@ -778,26 +775,34 @@ func (eb *EthBroadcaster) tryAgainBumpingGas(ctx context.Context, lgr logger.Log func (eb *EthBroadcaster) tryAgainBumpingLegacyGas(ctx context.Context, lgr logger.Logger, etx EthTx, attempt EthTxAttempt, initialBroadcastAt time.Time) (err error, retryable bool) { keySpecificMaxGasPriceWei := eb.config.KeySpecificMaxGasPriceWei(etx.FromAddress) - bumpedGasPrice, bumpedGasLimit, err := eb.estimator.BumpLegacyGas(ctx, attempt.GasPrice, etx.GasLimit, keySpecificMaxGasPriceWei, nil) + bumpedGasPrice, bumpedGasLimit, err := eb.estimator.BumpFee(ctx, gas.EvmFee{Legacy: attempt.GasPrice}, etx.GasLimit, keySpecificMaxGasPriceWei, nil) if err != nil { return errors.Wrap(err, "tryAgainBumpingLegacyGas failed"), true } - if bumpedGasPrice.Cmp(attempt.GasPrice) == 0 || bumpedGasPrice.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 { - return errors.Errorf("hit gas price bump ceiling, will not bump further"), true // TODO: Is this terminal or retryable? Is it possible to send unsaved attempts here? - } - return eb.tryAgainWithNewLegacyGas(ctx, lgr, etx, attempt, initialBroadcastAt, bumpedGasPrice, bumpedGasLimit) + + // NOTE: commented out - err handling before catches when price bump ceiling is hit + // TODO: remove + // if bumpedGasPrice.Cmp(attempt.GasPrice) == 0 || bumpedGasPrice.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 { + // return errors.Errorf("hit gas price bump ceiling, will not bump further"), true // TODO: Is this terminal or retryable? Is it possible to send unsaved attempts here? + // } + return eb.tryAgainWithNewLegacyGas(ctx, lgr, etx, attempt, initialBroadcastAt, bumpedGasPrice.Legacy, bumpedGasLimit) } func (eb *EthBroadcaster) tryAgainBumpingDynamicFeeGas(ctx context.Context, lgr logger.Logger, etx EthTx, attempt EthTxAttempt, initialBroadcastAt time.Time) (err error, retryable bool) { keySpecificMaxGasPriceWei := eb.config.KeySpecificMaxGasPriceWei(etx.FromAddress) - bumpedFee, bumpedGasLimit, err := eb.estimator.BumpDynamicFee(ctx, attempt.DynamicFee(), etx.GasLimit, keySpecificMaxGasPriceWei, nil) + + prevFee := attempt.DynamicFee() // TODO: make a method for tx to construct + bumpedFee, bumpedGasLimit, err := eb.estimator.BumpFee(ctx, gas.EvmFee{Dynamic: &prevFee}, etx.GasLimit, keySpecificMaxGasPriceWei, nil) if err != nil { return errors.Wrap(err, "tryAgainBumpingDynamicFeeGas failed"), true } - if bumpedFee.TipCap.Cmp(attempt.GasTipCap) == 0 || bumpedFee.FeeCap.Cmp(attempt.GasFeeCap) == 0 || bumpedFee.TipCap.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 || bumpedFee.TipCap.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 { - return errors.Errorf("hit gas price bump ceiling, will not bump further"), true // TODO: Is this terminal or retryable? Is it possible to send unsaved attempts here? - } - return eb.tryAgainWithNewDynamicFeeGas(ctx, lgr, etx, attempt, initialBroadcastAt, bumpedFee, bumpedGasLimit) + + // NOTE: commented out - err handling before catches when price bump ceiling is hit + // TODO: remove + // if bumpedFee.TipCap.Cmp(attempt.GasTipCap) == 0 || bumpedFee.FeeCap.Cmp(attempt.GasFeeCap) == 0 || bumpedFee.TipCap.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 || bumpedFee.TipCap.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 { + // return errors.Errorf("hit gas price bump ceiling, will not bump further"), true // TODO: Is this terminal or retryable? Is it possible to send unsaved attempts here? + // } + return eb.tryAgainWithNewDynamicFeeGas(ctx, lgr, etx, attempt, initialBroadcastAt, *bumpedFee.Dynamic, bumpedGasLimit) } func (eb *EthBroadcaster) tryAgainWithNewEstimation(ctx context.Context, lgr logger.Logger, sendError *evmclient.SendError, etx EthTx, attempt EthTxAttempt, initialBroadcastAt time.Time) (err error, retryable bool) { @@ -807,13 +812,15 @@ func (eb *EthBroadcaster) tryAgainWithNewEstimation(ctx context.Context, lgr log return err, false } keySpecificMaxGasPriceWei := eb.config.KeySpecificMaxGasPriceWei(etx.FromAddress) - gasPrice, gasLimit, err := eb.estimator.GetLegacyGas(ctx, etx.EncodedPayload, etx.GasLimit, keySpecificMaxGasPriceWei, gas.OptForceRefetch) + gasPrice, gasLimit, err := eb.estimator.GetFee(ctx, etx.EncodedPayload, etx.GasLimit, keySpecificMaxGasPriceWei, gas.OptForceRefetch) if err != nil { return errors.Wrap(err, "tryAgainWithNewEstimation failed to estimate gas"), true } lgr.Warnw("L2 rejected transaction due to incorrect fee, re-estimated and will try again", "etxID", etx.ID, "err", err, "newGasPrice", gasPrice, "newGasLimit", gasLimit) - return eb.tryAgainWithNewLegacyGas(ctx, lgr, etx, attempt, initialBroadcastAt, gasPrice, gasLimit) + + // TODO: nil handling for gasPrice.Legacy? will this ever be reached where EIP1559 is enabled on a L2 but a legacy tx? + return eb.tryAgainWithNewLegacyGas(ctx, lgr, etx, attempt, initialBroadcastAt, gasPrice.Legacy, gasLimit) } func (eb *EthBroadcaster) tryAgainWithNewLegacyGas(ctx context.Context, lgr logger.Logger, etx EthTx, attempt EthTxAttempt, initialBroadcastAt time.Time, newGasPrice *assets.Wei, newGasLimit uint32) (err error, retyrable bool) { diff --git a/core/chains/evm/txmgr/eth_confirmer.go b/core/chains/evm/txmgr/eth_confirmer.go index ea642db7571..c661334af93 100644 --- a/core/chains/evm/txmgr/eth_confirmer.go +++ b/core/chains/evm/txmgr/eth_confirmer.go @@ -117,7 +117,7 @@ type EthConfirmer struct { lggr logger.Logger ethClient evmclient.Client ChainKeyStore - estimator gas.Estimator + estimator gas.FeeEstimator resumeCallback ResumeCallback keyStates []ethkey.State @@ -131,8 +131,8 @@ type EthConfirmer struct { } // NewEthConfirmer instantiates a new eth confirmer -func NewEthConfirmer(orm ORM, ethClient evmclient.Client, config Config, keystore KeyStore, - keyStates []ethkey.State, estimator gas.Estimator, resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { +func NewEthConfirmer(orm ORM ethClient evmclient.Client, config Config, keystore KeyStore, + keyStates []ethkey.State, estimator gas.FeeEstimator, resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { ctx, cancel := context.WithCancel(context.Background()) lggr = lggr.Named("EthConfirmer") @@ -769,23 +769,23 @@ func (ec *EthConfirmer) bumpGas(ctx context.Context, etx EthTx, previousAttempts keySpecificMaxGasPriceWei := ec.config.KeySpecificMaxGasPriceWei(etx.FromAddress) switch previousAttempt.TxType { case 0x0: // Legacy - var bumpedGasPrice *assets.Wei + var bumpedFee gas.EvmFee var bumpedGasLimit uint32 - bumpedGasPrice, bumpedGasLimit, err = ec.estimator.BumpLegacyGas(ctx, previousAttempt.GasPrice, etx.GasLimit, keySpecificMaxGasPriceWei, priorAttempts) + bumpedFee, bumpedGasLimit, err = ec.estimator.BumpFee(ctx, gas.EvmFee{Legacy: previousAttempt.GasPrice}, etx.GasLimit, keySpecificMaxGasPriceWei, priorAttempts) if err == nil { promNumGasBumps.WithLabelValues(ec.chainID.String()).Inc() - ec.lggr.Debugw("Rebroadcast bumping gas for Legacy tx", append(logFields, "bumpedGasPrice", bumpedGasPrice.String())...) - return ec.NewLegacyAttempt(etx, bumpedGasPrice, bumpedGasLimit) + ec.lggr.Debugw("Rebroadcast bumping gas for Legacy tx", append(logFields, "bumpedGasPrice", bumpedFee.Legacy.String())...) + return ec.NewLegacyAttempt(etx, bumpedFee.Legacy, bumpedGasLimit) } case 0x2: // EIP1559 - var bumpedFee gas.DynamicFee + var bumpedFee gas.EvmFee var bumpedGasLimit uint32 original := previousAttempt.DynamicFee() - bumpedFee, bumpedGasLimit, err = ec.estimator.BumpDynamicFee(ctx, original, etx.GasLimit, keySpecificMaxGasPriceWei, priorAttempts) + bumpedFee, bumpedGasLimit, err = ec.estimator.BumpFee(ctx, gas.EvmFee{Dynamic: &original}, etx.GasLimit, keySpecificMaxGasPriceWei, priorAttempts) if err == nil { promNumGasBumps.WithLabelValues(ec.chainID.String()).Inc() - ec.lggr.Debugw("Rebroadcast bumping gas for DynamicFee tx", append(logFields, "bumpedTipCap", bumpedFee.TipCap.String(), "bumpedFeeCap", bumpedFee.FeeCap.String())...) - return ec.NewDynamicFeeAttempt(etx, bumpedFee, bumpedGasLimit) + ec.lggr.Debugw("Rebroadcast bumping gas for DynamicFee tx", append(logFields, "bumpedTipCap", bumpedFee.Dynamic.TipCap.String(), "bumpedFeeCap", bumpedFee.Dynamic.FeeCap.String())...) + return ec.NewDynamicFeeAttempt(etx, *bumpedFee.Dynamic, bumpedGasLimit) } default: err = errors.Errorf("invariant violation: Attempt %v had unrecognised transaction type %v"+ diff --git a/core/chains/evm/txmgr/txmgr.go b/core/chains/evm/txmgr/txmgr.go index 79d21805260..a1dc7a0570c 100644 --- a/core/chains/evm/txmgr/txmgr.go +++ b/core/chains/evm/txmgr/txmgr.go @@ -79,7 +79,7 @@ type TxManager interface { Trigger(addr common.Address) CreateEthTransaction(newTx NewTx, qopts ...pg.QOpt) (etx EthTx, err error) GetForwarderForEOA(eoa common.Address) (forwarder common.Address, err error) - GetGasEstimator() gas.Estimator + GetGasEstimator() gas.FeeEstimator RegisterResumeCallback(fn ResumeCallback) SendEther(chainID *big.Int, from, to common.Address, value assets.Eth, gasLimit uint32) (etx EthTx, err error) Reset(f func(), addr common.Address, abandon bool) error @@ -104,7 +104,7 @@ type Txm struct { config Config keyStore KeyStore eventBroadcaster pg.EventBroadcaster - gasEstimator gas.Estimator + gasEstimator gas.FeeEstimator chainID big.Int checkerFactory TransmitCheckerFactory @@ -556,7 +556,7 @@ func (b *Txm) checkEnabled(addr common.Address) error { } // GetGasEstimator returns the gas estimator, mostly useful for tests -func (b *Txm) GetGasEstimator() gas.Estimator { +func (b *Txm) GetGasEstimator() gas.FeeEstimator { return b.gasEstimator } @@ -728,5 +728,5 @@ func (n *NullTxManager) Healthy() error { return nil } func (n *NullTxManager) Ready() error { return nil } func (n *NullTxManager) Name() string { return "" } func (n *NullTxManager) HealthReport() map[string]error { return nil } -func (n *NullTxManager) GetGasEstimator() gas.Estimator { return nil } +func (n *NullTxManager) GetGasEstimator() gas.FeeEstimator { return nil } func (n *NullTxManager) RegisterResumeCallback(fn ResumeCallback) {} From dd078befb4be68615f35913b100195829981e2d2 Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Fri, 24 Feb 2023 15:04:26 -0700 Subject: [PATCH 02/10] fix components that use txm estimator + regenerate mocks --- core/chains/evm/txmgr/mocks/tx_manager.go | 8 ++++---- core/services/keeper/upkeep_executer.go | 4 ++-- .../reasonable_gas_price_provider.go | 4 ++-- core/web/evm_transfer_controller.go | 11 +++++++++-- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/core/chains/evm/txmgr/mocks/tx_manager.go b/core/chains/evm/txmgr/mocks/tx_manager.go index 304f13f31d7..2ce09fea678 100644 --- a/core/chains/evm/txmgr/mocks/tx_manager.go +++ b/core/chains/evm/txmgr/mocks/tx_manager.go @@ -99,15 +99,15 @@ func (_m *TxManager) GetForwarderForEOA(eoa common.Address) (common.Address, err } // GetGasEstimator provides a mock function with given fields: -func (_m *TxManager) GetGasEstimator() gas.Estimator { +func (_m *TxManager) GetGasEstimator() gas.FeeEstimator { ret := _m.Called() - var r0 gas.Estimator - if rf, ok := ret.Get(0).(func() gas.Estimator); ok { + var r0 gas.FeeEstimator + if rf, ok := ret.Get(0).(func() gas.FeeEstimator); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(gas.Estimator) + r0 = ret.Get(0).(gas.FeeEstimator) } } diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index 92f4e3d58ed..5a78f63cfde 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -51,7 +51,7 @@ type UpkeepExecuter struct { config Config executionQueue chan struct{} headBroadcaster httypes.HeadBroadcasterRegistry - gasEstimator gas.Estimator + gasEstimator gas.FeeEstimator job job.Job mailbox *utils.Mailbox[*evmtypes.Head] orm ORM @@ -69,7 +69,7 @@ func NewUpkeepExecuter( pr pipeline.Runner, ethClient evmclient.Client, headBroadcaster httypes.HeadBroadcaster, - gasEstimator gas.Estimator, + gasEstimator gas.FeeEstimator, logger logger.Logger, config Config, effectiveKeeperAddress common.Address, diff --git a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go index cbe0fd98c6f..28ede14fc21 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go +++ b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go @@ -12,7 +12,7 @@ import ( // reasonableGasPriceProvider provides an estimate for the average gas price type reasonableGasPriceProvider struct { - estimator gas.Estimator + estimator gas.FeeEstimator timeout time.Duration maxGasPrice *assets.Wei supportsDynamicFee bool @@ -21,7 +21,7 @@ type reasonableGasPriceProvider struct { var _ types.ReasonableGasPrice = (*reasonableGasPriceProvider)(nil) func NewReasonableGasPriceProvider( - estimator gas.Estimator, + estimator gas.FeeEstimator, timeout time.Duration, maxGasPrice *assets.Wei, supportsDynamicFee bool, diff --git a/core/web/evm_transfer_controller.go b/core/web/evm_transfer_controller.go index bc07bcd8c4f..87e4e0147af 100644 --- a/core/web/evm_transfer_controller.go +++ b/core/web/evm_transfer_controller.go @@ -9,6 +9,7 @@ import ( "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm" + "github.com/smartcontractkit/chainlink/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/core/logger/audit" "github.com/smartcontractkit/chainlink/core/services/chainlink" "github.com/smartcontractkit/chainlink/core/store/models" @@ -93,16 +94,22 @@ func ValidateEthBalanceForTransfer(c *gin.Context, chain evm.Chain, fromAddr com return errors.Errorf("balance is too low for this transaction to be executed: %v", balance) } - var gasPrice *assets.Wei + var fees gas.EvmFee gasLimit := chain.Config().EvmGasLimitTransfer() estimator := chain.TxManager().GetGasEstimator() - gasPrice, gasLimit, err = estimator.GetLegacyGas(c, nil, gasLimit, chain.Config().KeySpecificMaxGasPriceWei(fromAddr)) + fees, gasLimit, err = estimator.GetFee(c, nil, gasLimit, chain.Config().KeySpecificMaxGasPriceWei(fromAddr)) if err != nil { return errors.Wrap(err, "failed to estimate gas") } + // TODO: support EIP-1559 transactions + if fees.Legacy == nil { + return errors.New("estimator did not return legacy tx fee estimates") + } + gasPrice := fees.Legacy + // Creating a `Big` struct to avoid having a mutation on `tr.Amount` and hence affecting the value stored in the DB amountAsBig := utils.NewBig(amount.ToInt()) fee := new(big.Int).Mul(gasPrice.ToInt(), big.NewInt(int64(gasLimit))) From 68f7dad3883d31aa819b36725ee38cea42823f14 Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Mon, 27 Feb 2023 16:11:37 -0700 Subject: [PATCH 03/10] fix tests --- core/chains/evm/gas/mocks/fee_estimator.go | 137 ++++++++++++++++++ core/chains/evm/gas/models.go | 3 + core/chains/evm/txmgr/eth_broadcaster_test.go | 8 +- core/chains/evm/txmgr/eth_confirmer.go | 2 +- core/chains/evm/txmgr/txmgr_test.go | 2 +- core/internal/cltest/cltest.go | 4 +- core/internal/features/features_test.go | 8 +- core/services/keeper/upkeep_executer_test.go | 15 +- 8 files changed, 160 insertions(+), 19 deletions(-) create mode 100644 core/chains/evm/gas/mocks/fee_estimator.go diff --git a/core/chains/evm/gas/mocks/fee_estimator.go b/core/chains/evm/gas/mocks/fee_estimator.go new file mode 100644 index 00000000000..705bbdb632a --- /dev/null +++ b/core/chains/evm/gas/mocks/fee_estimator.go @@ -0,0 +1,137 @@ +// Code generated by mockery v2.20.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + assets "github.com/smartcontractkit/chainlink/core/assets" + + gas "github.com/smartcontractkit/chainlink/core/chains/evm/gas" + + mock "github.com/stretchr/testify/mock" + + types "github.com/smartcontractkit/chainlink/core/chains/evm/types" +) + +// FeeEstimator is an autogenerated mock type for the FeeEstimator type +type FeeEstimator struct { + mock.Mock +} + +// BumpFee provides a mock function with given fields: ctx, originalFee, gasLimit, maxGasPriceWei, attempts +func (_m *FeeEstimator) BumpFee(ctx context.Context, originalFee gas.EvmFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.PriorAttempt) (gas.EvmFee, uint32, error) { + ret := _m.Called(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + + var r0 gas.EvmFee + var r1 uint32 + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, gas.EvmFee, uint32, *assets.Wei, []gas.PriorAttempt) (gas.EvmFee, uint32, error)); ok { + return rf(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + } + if rf, ok := ret.Get(0).(func(context.Context, gas.EvmFee, uint32, *assets.Wei, []gas.PriorAttempt) gas.EvmFee); ok { + r0 = rf(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + } else { + r0 = ret.Get(0).(gas.EvmFee) + } + + if rf, ok := ret.Get(1).(func(context.Context, gas.EvmFee, uint32, *assets.Wei, []gas.PriorAttempt) uint32); ok { + r1 = rf(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + } else { + r1 = ret.Get(1).(uint32) + } + + if rf, ok := ret.Get(2).(func(context.Context, gas.EvmFee, uint32, *assets.Wei, []gas.PriorAttempt) error); ok { + r2 = rf(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// Close provides a mock function with given fields: +func (_m *FeeEstimator) Close() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetFee provides a mock function with given fields: ctx, calldata, gasLimit, maxGasPriceWei, opts +func (_m *FeeEstimator) GetFee(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...gas.Opt) (gas.EvmFee, uint32, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, calldata, gasLimit, maxGasPriceWei) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 gas.EvmFee + var r1 uint32 + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) (gas.EvmFee, uint32, error)); ok { + return rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) gas.EvmFee); ok { + r0 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + } else { + r0 = ret.Get(0).(gas.EvmFee) + } + + if rf, ok := ret.Get(1).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) uint32); ok { + r1 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + } else { + r1 = ret.Get(1).(uint32) + } + + if rf, ok := ret.Get(2).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) error); ok { + r2 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + +// OnNewLongestChain provides a mock function with given fields: _a0, _a1 +func (_m *FeeEstimator) OnNewLongestChain(_a0 context.Context, _a1 *types.Head) { + _m.Called(_a0, _a1) +} + +// Start provides a mock function with given fields: _a0 +func (_m *FeeEstimator) Start(_a0 context.Context) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +type mockConstructorTestingTNewFeeEstimator interface { + mock.TestingT + Cleanup(func()) +} + +// NewFeeEstimator creates a new instance of FeeEstimator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewFeeEstimator(t mockConstructorTestingTNewFeeEstimator) *FeeEstimator { + mock := &FeeEstimator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index eadd7797a64..6a57e74d6f5 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -111,6 +111,9 @@ type EvmFee struct { Dynamic *DynamicFee } +// Estimator provides an interface that wraps the EVM specific dynamic and legacy estimators into one estimator +// +//go:generate mockery --quiet --name FeeEstimator --output ./mocks/ --case=underscore type FeeEstimator interface { OnNewLongestChain(context.Context, *evmtypes.Head) Start(context.Context) error diff --git a/core/chains/evm/txmgr/eth_broadcaster_test.go b/core/chains/evm/txmgr/eth_broadcaster_test.go index 5628d9243b7..ac6b7c21af3 100644 --- a/core/chains/evm/txmgr/eth_broadcaster_test.go +++ b/core/chains/evm/txmgr/eth_broadcaster_test.go @@ -576,8 +576,8 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi chStartEstimate := make(chan struct{}) chBlock := make(chan struct{}) - estimator := gasmocks.NewEstimator(t) - estimator.On("GetLegacyGas", mock.Anything, mock.Anything, mock.Anything, evmcfg.KeySpecificMaxGasPriceWei(fromAddress)).Return(assets.GWei(32), uint32(500), nil).Run(func(_ mock.Arguments) { + estimator := gasmocks.NewFeeEstimator(t) + estimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, evmcfg.KeySpecificMaxGasPriceWei(fromAddress)).Return(gas.EvmFee{Legacy: assets.GWei(32)}, uint32(500), nil).Run(func(_ mock.Arguments) { close(chStartEstimate) <-chBlock }) @@ -1140,7 +1140,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_Errors(t *testing.T) { t.Cleanup(func() { assert.NoError(t, eventBroadcaster.Close()) }) lggr := logger.TestLogger(t) eb := txmgr.NewEthBroadcaster(db, ethClient, evmcfg, ethKeyStore, eventBroadcaster, - []ethkey.State{keyState}, gas.NewFixedPriceEstimator(evmcfg, lggr), fn, lggr, + []ethkey.State{keyState}, gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(evmcfg, lggr), evmcfg), fn, lggr, &testCheckerFactory{}, false) { @@ -1887,7 +1887,7 @@ func TestEthBroadcaster_SyncNonce(t *testing.T) { sub.On("Events").Return(make(<-chan pg.Event)) sub.On("Close") eventBroadcaster.On("Subscribe", "insert_on_eth_txes", "").Return(sub, nil) - estimator := gas.NewFixedPriceEstimator(evmcfg, lggr) + estimator := gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(evmcfg, lggr), evmcfg) checkerFactory := &testCheckerFactory{} t.Run("does nothing if nonce sync is disabled", func(t *testing.T) { diff --git a/core/chains/evm/txmgr/eth_confirmer.go b/core/chains/evm/txmgr/eth_confirmer.go index c661334af93..bcda0a13d33 100644 --- a/core/chains/evm/txmgr/eth_confirmer.go +++ b/core/chains/evm/txmgr/eth_confirmer.go @@ -131,7 +131,7 @@ type EthConfirmer struct { } // NewEthConfirmer instantiates a new eth confirmer -func NewEthConfirmer(orm ORM ethClient evmclient.Client, config Config, keystore KeyStore, +func NewEthConfirmer(orm ORM, ethClient evmclient.Client, config Config, keystore KeyStore, keyStates []ethkey.State, estimator gas.FeeEstimator, resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { ctx, cancel := context.WithCancel(context.Background()) diff --git a/core/chains/evm/txmgr/txmgr_test.go b/core/chains/evm/txmgr/txmgr_test.go index 17c209331a7..bb97462d80b 100644 --- a/core/chains/evm/txmgr/txmgr_test.go +++ b/core/chains/evm/txmgr/txmgr_test.go @@ -457,7 +457,7 @@ func newMockConfig(t *testing.T) *txmmocks.Config { cfg.On("BlockHistoryEstimatorBlockHistorySize").Return(uint16(42)).Maybe().Once() cfg.On("BlockHistoryEstimatorEIP1559FeeCapBufferBlocks").Return(uint16(42)).Maybe().Once() cfg.On("BlockHistoryEstimatorTransactionPercentile").Return(uint16(42)).Maybe().Once() - cfg.On("EvmEIP1559DynamicFees").Return(false).Maybe().Once() + cfg.On("EvmEIP1559DynamicFees").Return(false).Maybe().Twice() cfg.On("EvmGasBumpPercent").Return(uint16(42)).Maybe().Once() cfg.On("EvmGasBumpThreshold").Return(uint64(42)).Maybe() cfg.On("EvmGasBumpWei").Return(assets.NewWeiI(42)).Maybe().Once() diff --git a/core/internal/cltest/cltest.go b/core/internal/cltest/cltest.go index e1ba1c8d872..fdda729bf6b 100644 --- a/core/internal/cltest/cltest.go +++ b/core/internal/cltest/cltest.go @@ -208,7 +208,7 @@ func NewEthBroadcaster(t testing.TB, db *sqlx.DB, ethClient evmclient.Client, ke t.Cleanup(func() { assert.NoError(t, eventBroadcaster.Close()) }) lggr := logger.TestLogger(t) return txmgr.NewEthBroadcaster(db, ethClient, config, keyStore, eventBroadcaster, - keyStates, gas.NewFixedPriceEstimator(config, lggr), nil, lggr, + keyStates, gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(config, lggr), config), nil, lggr, checkerFactory, nonceAutoSync) } @@ -221,7 +221,7 @@ func NewEthConfirmer(t testing.TB, orm txmgr.ORM, ethClient evmclient.Client, co t.Helper() lggr := logger.TestLogger(t) ec := txmgr.NewEthConfirmer(orm, ethClient, config, ks, keyStates, - gas.NewFixedPriceEstimator(config, lggr), fn, lggr) + gas.NewWrappedEvmEstimator(gas.NewFixedPriceEstimator(config, lggr), config), fn, lggr) return ec } diff --git a/core/internal/features/features_test.go b/core/internal/features/features_test.go index dec113f11b6..50250b642d0 100644 --- a/core/internal/features/features_test.go +++ b/core/internal/features/features_test.go @@ -1379,10 +1379,10 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { chain := evmtest.MustGetDefaultChain(t, cc) estimator := chain.TxManager().GetGasEstimator() - gasPrice, gasLimit, err := estimator.GetLegacyGas(testutils.Context(t), nil, 500_000, maxGasPrice) + gasPrice, gasLimit, err := estimator.GetFee(testutils.Context(t), nil, 500_000, maxGasPrice) require.NoError(t, err) assert.Equal(t, uint32(500000), gasLimit) - assert.Equal(t, "41.5 gwei", gasPrice.String()) + assert.Equal(t, "41.5 gwei", gasPrice.Legacy.String()) assert.Equal(t, initialDefaultGasPrice, chain.Config().EvmGasPriceDefault().Int64()) // unchanged // BlockHistoryEstimator new blocks @@ -1401,9 +1401,9 @@ func TestIntegration_BlockHistoryEstimator(t *testing.T) { newHeads.TrySend(cltest.Head(43)) gomega.NewWithT(t).Eventually(func() string { - gasPrice, _, err := estimator.GetLegacyGas(testutils.Context(t), nil, 500000, maxGasPrice) + gasPrice, _, err := estimator.GetFee(testutils.Context(t), nil, 500000, maxGasPrice) require.NoError(t, err) - return gasPrice.String() + return gasPrice.Legacy.String() }, testutils.WaitTimeout(t), cltest.DBPollingInterval).Should(gomega.Equal("45 gwei")) } diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 198c70228e7..4e2d680d800 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -40,17 +40,18 @@ func newHead() evmtypes.Head { return evmtypes.NewHead(big.NewInt(20), utils.NewHash(), utils.NewHash(), 1000, utils.NewBigI(0)) } -func mockEstimator(t *testing.T) (estimator *gasmocks.Estimator) { - estimator = gasmocks.NewEstimator(t) - estimator.On("GetLegacyGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe().Return(assets.GWei(60), uint32(0), nil) - estimator.On("GetDynamicFee", mock.Anything, mock.Anything, mock.Anything).Maybe().Return(gas.DynamicFee{ - FeeCap: assets.GWei(60), - TipCap: assets.GWei(60), +func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator) { + // note: estimator will only return 1 of legacy or dynamic fees (not both) + // assumed to call legacy estimator only + estimator = gasmocks.NewFeeEstimator(t) + estimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe().Return(gas.EvmFee{ + Legacy: assets.GWei(60), + Dynamic: nil, }, uint32(60), nil) return } -func setup(t *testing.T, estimator *gasmocks.Estimator, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) ( +func setup(t *testing.T, estimator *gasmocks.FeeEstimator, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) ( *sqlx.DB, config.GeneralConfig, *evmmocks.Client, From ce243aafe755a35f63ad6837b5cf65776a6a0752 Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Tue, 28 Feb 2023 10:44:17 -0700 Subject: [PATCH 04/10] old Estimator => EvmEstimator --- core/chains/evm/gas/arbitrum_estimator.go | 12 ++++----- .../chains/evm/gas/block_history_estimator.go | 4 +-- .../evm/gas/block_history_estimator_test.go | 2 +- core/chains/evm/gas/cmd/arbgas/main.go | 6 ++--- core/chains/evm/gas/fixed_price_estimator.go | 4 +-- core/chains/evm/gas/helpers_test.go | 6 ++--- core/chains/evm/gas/l2_suggested_estimator.go | 4 +-- .../mocks/{estimator.go => evm_estimator.go} | 26 +++++++++---------- core/chains/evm/gas/models.go | 18 ++++++------- 9 files changed, 41 insertions(+), 41 deletions(-) rename core/chains/evm/gas/mocks/{estimator.go => evm_estimator.go} (80%) diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index 6d3438a5dc8..dbd8ca8b19f 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -34,7 +34,7 @@ type arbitrumEstimator struct { cfg ArbConfig - Estimator // *l2SuggestedPriceEstimator + EvmEstimator // *l2SuggestedPriceEstimator client ethClient pollPeriod time.Duration @@ -50,11 +50,11 @@ type arbitrumEstimator struct { chDone chan struct{} } -func NewArbitrumEstimator(lggr logger.Logger, cfg ArbConfig, rpcClient rpcClient, ethClient ethClient) Estimator { +func NewArbitrumEstimator(lggr logger.Logger, cfg ArbConfig, rpcClient rpcClient, ethClient ethClient) EvmEstimator { lggr = lggr.Named("ArbitrumEstimator") return &arbitrumEstimator{ cfg: cfg, - Estimator: NewL2SuggestedPriceEstimator(lggr, rpcClient), + EvmEstimator: NewL2SuggestedPriceEstimator(lggr, rpcClient), client: ethClient, pollPeriod: 10 * time.Second, logger: lggr, @@ -67,7 +67,7 @@ func NewArbitrumEstimator(lggr logger.Logger, cfg ArbConfig, rpcClient rpcClient func (a *arbitrumEstimator) Start(ctx context.Context) error { return a.StartOnce("ArbitrumEstimator", func() error { - if err := a.Estimator.Start(ctx); err != nil { + if err := a.EvmEstimator.Start(ctx); err != nil { return errors.Wrap(err, "failed to start gas price estimator") } go a.run() @@ -78,7 +78,7 @@ func (a *arbitrumEstimator) Start(ctx context.Context) error { func (a *arbitrumEstimator) Close() error { return a.StopOnce("ArbitrumEstimator", func() (err error) { close(a.chStop) - err = errors.Wrap(a.Estimator.Close(), "failed to stop gas price estimator") + err = errors.Wrap(a.EvmEstimator.Close(), "failed to stop gas price estimator") <-a.chDone return }) @@ -90,7 +90,7 @@ func (a *arbitrumEstimator) Close() error { // of the precompilie contract at ArbGasInfoAddress. perL2Tx is a constant amount of gas, and perL1CalldataUnit is // multiplied by the length of the tx calldata. The sum of these two values plus the original l2GasLimit is returned. func (a *arbitrumEstimator) GetLegacyGas(ctx context.Context, calldata []byte, l2GasLimit uint32, maxGasPriceWei *assets.Wei, opts ...Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { - gasPrice, _, err = a.Estimator.GetLegacyGas(ctx, calldata, l2GasLimit, maxGasPriceWei, opts...) + gasPrice, _, err = a.EvmEstimator.GetLegacyGas(ctx, calldata, l2GasLimit, maxGasPriceWei, opts...) if err != nil { return } diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 6c2ada127ca..7c4547bf4e0 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -74,7 +74,7 @@ const BumpingHaltedLabel = "Tx gas bumping halted since price exceeds current bl var ErrConnectivity = errors.New("transaction propagation issue: transactions are not being mined") -var _ Estimator = &BlockHistoryEstimator{} +var _ EvmEstimator = &BlockHistoryEstimator{} //go:generate mockery --quiet --name Config --output ./mocks/ --case=underscore type ( @@ -107,7 +107,7 @@ type ( // NewBlockHistoryEstimator returns a new BlockHistoryEstimator that listens // for new heads and updates the base gas price dynamically based on the // configured percentile of gas prices in that block -func NewBlockHistoryEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config, chainID big.Int) Estimator { +func NewBlockHistoryEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config, chainID big.Int) EvmEstimator { ctx, cancel := context.WithCancel(context.Background()) b := &BlockHistoryEstimator{ ethClient: ethClient, diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index 676d25e338b..1f96b59a0bc 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -43,7 +43,7 @@ func newConfigWithEIP1559DynamicFeesDisabled(t *testing.T) *gas.MockConfig { return cfg } -func newBlockHistoryEstimatorWithChainID(t *testing.T, c evmclient.Client, cfg gas.Config, cid big.Int) gas.Estimator { +func newBlockHistoryEstimatorWithChainID(t *testing.T, c evmclient.Client, cfg gas.Config, cid big.Int) gas.EvmEstimator { return gas.NewBlockHistoryEstimator(logger.TestLogger(t), c, cfg, cid) } diff --git a/core/chains/evm/gas/cmd/arbgas/main.go b/core/chains/evm/gas/cmd/arbgas/main.go index 10f1f864865..708d2b61fa7 100644 --- a/core/chains/evm/gas/cmd/arbgas/main.go +++ b/core/chains/evm/gas/cmd/arbgas/main.go @@ -26,14 +26,14 @@ func main() { lggr.SetLogLevel(zapcore.DebugLevel) ctx := context.Background() - withEstimator(ctx, logger.Sugared(lggr), url, func(e gas.Estimator) { + withEstimator(ctx, logger.Sugared(lggr), url, func(e gas.EvmEstimator) { printGetLegacyGas(ctx, e, make([]byte, 10), 500_000, assets.GWei(1)) printGetLegacyGas(ctx, e, make([]byte, 10), 500_000, assets.GWei(1), gas.OptForceRefetch) printGetLegacyGas(ctx, e, make([]byte, 10), max, assets.GWei(1)) }) } -func printGetLegacyGas(ctx context.Context, e gas.Estimator, calldata []byte, l2GasLimit uint32, maxGasPrice *assets.Wei, opts ...gas.Opt) { +func printGetLegacyGas(ctx context.Context, e gas.EvmEstimator, calldata []byte, l2GasLimit uint32, maxGasPrice *assets.Wei, opts ...gas.Opt) { price, limit, err := e.GetLegacyGas(ctx, calldata, l2GasLimit, maxGasPrice, opts...) if err != nil { log.Println("failed to get legacy gas:", err) @@ -45,7 +45,7 @@ func printGetLegacyGas(ctx context.Context, e gas.Estimator, calldata []byte, l2 const max = 50_000_000 -func withEstimator(ctx context.Context, lggr logger.SugaredLogger, url string, f func(e gas.Estimator)) { +func withEstimator(ctx context.Context, lggr logger.SugaredLogger, url string, f func(e gas.EvmEstimator)) { rc, err := rpc.Dial(url) if err != nil { log.Fatal(err) diff --git a/core/chains/evm/gas/fixed_price_estimator.go b/core/chains/evm/gas/fixed_price_estimator.go index 0c970ccd870..71efcbc88b8 100644 --- a/core/chains/evm/gas/fixed_price_estimator.go +++ b/core/chains/evm/gas/fixed_price_estimator.go @@ -10,7 +10,7 @@ import ( "github.com/smartcontractkit/chainlink/core/logger" ) -var _ Estimator = &fixedPriceEstimator{} +var _ EvmEstimator = &fixedPriceEstimator{} type fixedPriceEstimator struct { config Config @@ -19,7 +19,7 @@ type fixedPriceEstimator struct { // NewFixedPriceEstimator returns a new "FixedPrice" estimator which will // always use the config default values for gas prices and limits -func NewFixedPriceEstimator(cfg Config, lggr logger.Logger) Estimator { +func NewFixedPriceEstimator(cfg Config, lggr logger.Logger) EvmEstimator { return &fixedPriceEstimator{cfg, logger.Sugared(lggr.Named("FixedPriceEstimator"))} } diff --git a/core/chains/evm/gas/helpers_test.go b/core/chains/evm/gas/helpers_test.go index b4608220b10..2a4250c4c58 100644 --- a/core/chains/evm/gas/helpers_test.go +++ b/core/chains/evm/gas/helpers_test.go @@ -20,17 +20,17 @@ func (b *BlockHistoryEstimator) CheckConnectivity(attempts []PriorAttempt) error return b.checkConnectivity(attempts) } -func BlockHistoryEstimatorFromInterface(bhe Estimator) *BlockHistoryEstimator { +func BlockHistoryEstimatorFromInterface(bhe EvmEstimator) *BlockHistoryEstimator { return bhe.(*BlockHistoryEstimator) } -func SetRollingBlockHistory(bhe Estimator, blocks []evmtypes.Block) { +func SetRollingBlockHistory(bhe EvmEstimator, blocks []evmtypes.Block) { bhe.(*BlockHistoryEstimator).blocksMu.Lock() defer bhe.(*BlockHistoryEstimator).blocksMu.Unlock() bhe.(*BlockHistoryEstimator).blocks = blocks } -func GetRollingBlockHistory(bhe Estimator) []evmtypes.Block { +func GetRollingBlockHistory(bhe EvmEstimator) []evmtypes.Block { return bhe.(*BlockHistoryEstimator).getBlocks() } diff --git a/core/chains/evm/gas/l2_suggested_estimator.go b/core/chains/evm/gas/l2_suggested_estimator.go index 5f1e78d5340..5f7986443c4 100644 --- a/core/chains/evm/gas/l2_suggested_estimator.go +++ b/core/chains/evm/gas/l2_suggested_estimator.go @@ -17,7 +17,7 @@ import ( ) var ( - _ Estimator = &l2SuggestedPriceEstimator{} + _ EvmEstimator = &l2SuggestedPriceEstimator{} ) //go:generate mockery --quiet --name rpcClient --output ./mocks/ --case=underscore --structname RPCClient @@ -43,7 +43,7 @@ type l2SuggestedPriceEstimator struct { } // NewL2SuggestedPriceEstimator returns a new Estimator which uses the L2 suggested gas price. -func NewL2SuggestedPriceEstimator(lggr logger.Logger, client rpcClient) Estimator { +func NewL2SuggestedPriceEstimator(lggr logger.Logger, client rpcClient) EvmEstimator { return &l2SuggestedPriceEstimator{ client: client, pollPeriod: 10 * time.Second, diff --git a/core/chains/evm/gas/mocks/estimator.go b/core/chains/evm/gas/mocks/evm_estimator.go similarity index 80% rename from core/chains/evm/gas/mocks/estimator.go rename to core/chains/evm/gas/mocks/evm_estimator.go index f33da3f8519..96a9a5cf758 100644 --- a/core/chains/evm/gas/mocks/estimator.go +++ b/core/chains/evm/gas/mocks/evm_estimator.go @@ -14,13 +14,13 @@ import ( types "github.com/smartcontractkit/chainlink/core/chains/evm/types" ) -// Estimator is an autogenerated mock type for the Estimator type -type Estimator struct { +// EvmEstimator is an autogenerated mock type for the EvmEstimator type +type EvmEstimator struct { mock.Mock } // BumpDynamicFee provides a mock function with given fields: ctx, original, gasLimit, maxGasPriceWei, attempts -func (_m *Estimator) BumpDynamicFee(ctx context.Context, original gas.DynamicFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.PriorAttempt) (gas.DynamicFee, uint32, error) { +func (_m *EvmEstimator) BumpDynamicFee(ctx context.Context, original gas.DynamicFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.PriorAttempt) (gas.DynamicFee, uint32, error) { ret := _m.Called(ctx, original, gasLimit, maxGasPriceWei, attempts) var r0 gas.DynamicFee @@ -51,7 +51,7 @@ func (_m *Estimator) BumpDynamicFee(ctx context.Context, original gas.DynamicFee } // BumpLegacyGas provides a mock function with given fields: ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts -func (_m *Estimator) BumpLegacyGas(ctx context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.PriorAttempt) (*assets.Wei, uint32, error) { +func (_m *EvmEstimator) BumpLegacyGas(ctx context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.PriorAttempt) (*assets.Wei, uint32, error) { ret := _m.Called(ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts) var r0 *assets.Wei @@ -84,7 +84,7 @@ func (_m *Estimator) BumpLegacyGas(ctx context.Context, originalGasPrice *assets } // Close provides a mock function with given fields: -func (_m *Estimator) Close() error { +func (_m *EvmEstimator) Close() error { ret := _m.Called() var r0 error @@ -98,7 +98,7 @@ func (_m *Estimator) Close() error { } // GetDynamicFee provides a mock function with given fields: ctx, gasLimit, maxGasPriceWei -func (_m *Estimator) GetDynamicFee(ctx context.Context, gasLimit uint32, maxGasPriceWei *assets.Wei) (gas.DynamicFee, uint32, error) { +func (_m *EvmEstimator) GetDynamicFee(ctx context.Context, gasLimit uint32, maxGasPriceWei *assets.Wei) (gas.DynamicFee, uint32, error) { ret := _m.Called(ctx, gasLimit, maxGasPriceWei) var r0 gas.DynamicFee @@ -129,7 +129,7 @@ func (_m *Estimator) GetDynamicFee(ctx context.Context, gasLimit uint32, maxGasP } // GetLegacyGas provides a mock function with given fields: ctx, calldata, gasLimit, maxGasPriceWei, opts -func (_m *Estimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...gas.Opt) (*assets.Wei, uint32, error) { +func (_m *EvmEstimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...gas.Opt) (*assets.Wei, uint32, error) { _va := make([]interface{}, len(opts)) for _i := range opts { _va[_i] = opts[_i] @@ -169,12 +169,12 @@ func (_m *Estimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLimit } // OnNewLongestChain provides a mock function with given fields: _a0, _a1 -func (_m *Estimator) OnNewLongestChain(_a0 context.Context, _a1 *types.Head) { +func (_m *EvmEstimator) OnNewLongestChain(_a0 context.Context, _a1 *types.Head) { _m.Called(_a0, _a1) } // Start provides a mock function with given fields: _a0 -func (_m *Estimator) Start(_a0 context.Context) error { +func (_m *EvmEstimator) Start(_a0 context.Context) error { ret := _m.Called(_a0) var r0 error @@ -187,14 +187,14 @@ func (_m *Estimator) Start(_a0 context.Context) error { return r0 } -type mockConstructorTestingTNewEstimator interface { +type mockConstructorTestingTNewEvmEstimator interface { mock.TestingT Cleanup(func()) } -// NewEstimator creates a new instance of Estimator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewEstimator(t mockConstructorTestingTNewEstimator) *Estimator { - mock := &Estimator{} +// NewEvmEstimator creates a new instance of EvmEstimator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewEvmEstimator(t mockConstructorTestingTNewEvmEstimator) *EvmEstimator { + mock := &EvmEstimator{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index 6a57e74d6f5..ebbc46c138d 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -81,8 +81,8 @@ type PriorAttempt interface { // Estimator provides an interface for estimating gas price and limit // -//go:generate mockery --quiet --name Estimator --output ./mocks/ --case=underscore -type Estimator interface { +//go:generate mockery --quiet --name EvmEstimator --output ./mocks/ --case=underscore +type EvmEstimator interface { OnNewLongestChain(context.Context, *evmtypes.Head) Start(context.Context) error Close() error @@ -124,15 +124,15 @@ type FeeEstimator interface { } type WrappedEvmEstimator struct { - Estimator + EvmEstimator EIP1559Enabled bool } var _ FeeEstimator = (*WrappedEvmEstimator)(nil) -func NewWrappedEvmEstimator(e Estimator, cfg Config) FeeEstimator { +func NewWrappedEvmEstimator(e EvmEstimator, cfg Config) FeeEstimator { return &WrappedEvmEstimator{ - Estimator: e, + EvmEstimator: e, EIP1559Enabled: cfg.EvmEIP1559DynamicFees(), } } @@ -141,13 +141,13 @@ func (e WrappedEvmEstimator) GetFee(ctx context.Context, calldata []byte, gasLim // get dynamic fee if e.EIP1559Enabled { var dynamicFee DynamicFee - dynamicFee, chainSpecificGasLimit, err = e.Estimator.GetDynamicFee(ctx, gasLimit, maxGasPriceWei) + dynamicFee, chainSpecificGasLimit, err = e.EvmEstimator.GetDynamicFee(ctx, gasLimit, maxGasPriceWei) fee.Dynamic = &dynamicFee return } // get legacy fee - fee.Legacy, chainSpecificGasLimit, err = e.Estimator.GetLegacyGas(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + fee.Legacy, chainSpecificGasLimit, err = e.EvmEstimator.GetLegacyGas(ctx, calldata, gasLimit, maxGasPriceWei, opts...) return } @@ -162,13 +162,13 @@ func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, ga // bump dynamic original if originalFee.Dynamic != nil { var bumpedDynamic DynamicFee - bumpedDynamic, chainSpecificGasLimit, err = e.Estimator.BumpDynamicFee(ctx, *originalFee.Dynamic, gasLimit, maxGasPriceWei, attempts) + bumpedDynamic, chainSpecificGasLimit, err = e.EvmEstimator.BumpDynamicFee(ctx, *originalFee.Dynamic, gasLimit, maxGasPriceWei, attempts) bumpedFee.Dynamic = &bumpedDynamic return } // bump legacy fee - bumpedFee.Legacy, chainSpecificGasLimit, err = e.Estimator.BumpLegacyGas(ctx, originalFee.Legacy, gasLimit, maxGasPriceWei, attempts) + bumpedFee.Legacy, chainSpecificGasLimit, err = e.EvmEstimator.BumpLegacyGas(ctx, originalFee.Legacy, gasLimit, maxGasPriceWei, attempts) return } From e543b4325750cd897e77fb7a14c5c772c7a9936e Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Tue, 28 Feb 2023 12:00:36 -0700 Subject: [PATCH 05/10] condense gas estimation logic in broadcaster and confirmer --- core/chains/evm/txmgr/eth_broadcaster.go | 48 +++++++----------------- core/chains/evm/txmgr/eth_confirmer.go | 39 ++++++++++--------- core/chains/evm/txmgr/models.go | 11 ++++++ 3 files changed, 44 insertions(+), 54 deletions(-) diff --git a/core/chains/evm/txmgr/eth_broadcaster.go b/core/chains/evm/txmgr/eth_broadcaster.go index 94140ede9b6..2f22a0ba647 100644 --- a/core/chains/evm/txmgr/eth_broadcaster.go +++ b/core/chains/evm/txmgr/eth_broadcaster.go @@ -760,11 +760,23 @@ func (eb *EthBroadcaster) tryAgainBumpingGas(ctx context.Context, lgr logger.Log "Will bump and retry. ACTION REQUIRED: This is a configuration error. "+ "Consider increasing ETH_GAS_PRICE_DEFAULT (current value: %s)", attempt.GasPrice, sendError.Error(), eb.config.EvmGasPriceDefault().String()) + + keySpecificMaxGasPriceWei := eb.config.KeySpecificMaxGasPriceWei(etx.FromAddress) + bumpedFee, bumpedFeeLimit, err := eb.estimator.BumpFee(ctx, attempt.Fee(), etx.GasLimit, keySpecificMaxGasPriceWei, nil) + if err != nil { + return errors.Wrap(err, "tryAgainBumpFee failed"), true + } + switch attempt.TxType { case 0x0: - return eb.tryAgainBumpingLegacyGas(ctx, lgr, etx, attempt, initialBroadcastAt) + return eb.tryAgainWithNewLegacyGas(ctx, lgr, etx, attempt, initialBroadcastAt, bumpedFee.Legacy, bumpedFeeLimit) case 0x2: - return eb.tryAgainBumpingDynamicFeeGas(ctx, lgr, etx, attempt, initialBroadcastAt) + if bumpedFee.Dynamic == nil { + err = errors.Errorf("Attempt %v is a type 2 transaction but estimator did not return dynamic fee bump", attempt.ID) + logger.Sugared(eb.logger).AssumptionViolation(err.Error()) + return err, false + } + return eb.tryAgainWithNewDynamicFeeGas(ctx, lgr, etx, attempt, initialBroadcastAt, *bumpedFee.Dynamic, bumpedFeeLimit) default: err = errors.Errorf("invariant violation: Attempt %v had unrecognised transaction type %v"+ "This is a bug! Please report to https://github.com/smartcontractkit/chainlink/issues", attempt.ID, attempt.TxType) @@ -773,38 +785,6 @@ func (eb *EthBroadcaster) tryAgainBumpingGas(ctx context.Context, lgr logger.Log } } -func (eb *EthBroadcaster) tryAgainBumpingLegacyGas(ctx context.Context, lgr logger.Logger, etx EthTx, attempt EthTxAttempt, initialBroadcastAt time.Time) (err error, retryable bool) { - keySpecificMaxGasPriceWei := eb.config.KeySpecificMaxGasPriceWei(etx.FromAddress) - bumpedGasPrice, bumpedGasLimit, err := eb.estimator.BumpFee(ctx, gas.EvmFee{Legacy: attempt.GasPrice}, etx.GasLimit, keySpecificMaxGasPriceWei, nil) - if err != nil { - return errors.Wrap(err, "tryAgainBumpingLegacyGas failed"), true - } - - // NOTE: commented out - err handling before catches when price bump ceiling is hit - // TODO: remove - // if bumpedGasPrice.Cmp(attempt.GasPrice) == 0 || bumpedGasPrice.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 { - // return errors.Errorf("hit gas price bump ceiling, will not bump further"), true // TODO: Is this terminal or retryable? Is it possible to send unsaved attempts here? - // } - return eb.tryAgainWithNewLegacyGas(ctx, lgr, etx, attempt, initialBroadcastAt, bumpedGasPrice.Legacy, bumpedGasLimit) -} - -func (eb *EthBroadcaster) tryAgainBumpingDynamicFeeGas(ctx context.Context, lgr logger.Logger, etx EthTx, attempt EthTxAttempt, initialBroadcastAt time.Time) (err error, retryable bool) { - keySpecificMaxGasPriceWei := eb.config.KeySpecificMaxGasPriceWei(etx.FromAddress) - - prevFee := attempt.DynamicFee() // TODO: make a method for tx to construct - bumpedFee, bumpedGasLimit, err := eb.estimator.BumpFee(ctx, gas.EvmFee{Dynamic: &prevFee}, etx.GasLimit, keySpecificMaxGasPriceWei, nil) - if err != nil { - return errors.Wrap(err, "tryAgainBumpingDynamicFeeGas failed"), true - } - - // NOTE: commented out - err handling before catches when price bump ceiling is hit - // TODO: remove - // if bumpedFee.TipCap.Cmp(attempt.GasTipCap) == 0 || bumpedFee.FeeCap.Cmp(attempt.GasFeeCap) == 0 || bumpedFee.TipCap.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 || bumpedFee.TipCap.Cmp(eb.config.EvmMaxGasPriceWei()) >= 0 { - // return errors.Errorf("hit gas price bump ceiling, will not bump further"), true // TODO: Is this terminal or retryable? Is it possible to send unsaved attempts here? - // } - return eb.tryAgainWithNewDynamicFeeGas(ctx, lgr, etx, attempt, initialBroadcastAt, *bumpedFee.Dynamic, bumpedGasLimit) -} - func (eb *EthBroadcaster) tryAgainWithNewEstimation(ctx context.Context, lgr logger.Logger, sendError *evmclient.SendError, etx EthTx, attempt EthTxAttempt, initialBroadcastAt time.Time) (err error, retryable bool) { if attempt.TxType == 0x2 { err = errors.Errorf("re-estimation is not supported for EIP-1559 transactions. Eth node returned error: %v. This is a bug", sendError.Error()) diff --git a/core/chains/evm/txmgr/eth_confirmer.go b/core/chains/evm/txmgr/eth_confirmer.go index bcda0a13d33..922b5db84c8 100644 --- a/core/chains/evm/txmgr/eth_confirmer.go +++ b/core/chains/evm/txmgr/eth_confirmer.go @@ -767,29 +767,28 @@ func (ec *EthConfirmer) bumpGas(ctx context.Context, etx EthTx, previousAttempts previousAttempt := previousAttempts[0] logFields := ec.logFieldsPreviousAttempt(previousAttempt) keySpecificMaxGasPriceWei := ec.config.KeySpecificMaxGasPriceWei(etx.FromAddress) - switch previousAttempt.TxType { - case 0x0: // Legacy - var bumpedFee gas.EvmFee - var bumpedGasLimit uint32 - bumpedFee, bumpedGasLimit, err = ec.estimator.BumpFee(ctx, gas.EvmFee{Legacy: previousAttempt.GasPrice}, etx.GasLimit, keySpecificMaxGasPriceWei, priorAttempts) - if err == nil { - promNumGasBumps.WithLabelValues(ec.chainID.String()).Inc() + + var bumpedFee gas.EvmFee + var bumpedFeeLimit uint32 + bumpedFee, bumpedFeeLimit, err = ec.estimator.BumpFee(ctx, previousAttempt.Fee(), etx.GasLimit, keySpecificMaxGasPriceWei, priorAttempts) + + if err == nil { + promNumGasBumps.WithLabelValues(ec.chainID.String()).Inc() + switch previousAttempt.TxType { + case 0x0: // Legacy ec.lggr.Debugw("Rebroadcast bumping gas for Legacy tx", append(logFields, "bumpedGasPrice", bumpedFee.Legacy.String())...) - return ec.NewLegacyAttempt(etx, bumpedFee.Legacy, bumpedGasLimit) - } - case 0x2: // EIP1559 - var bumpedFee gas.EvmFee - var bumpedGasLimit uint32 - original := previousAttempt.DynamicFee() - bumpedFee, bumpedGasLimit, err = ec.estimator.BumpFee(ctx, gas.EvmFee{Dynamic: &original}, etx.GasLimit, keySpecificMaxGasPriceWei, priorAttempts) - if err == nil { - promNumGasBumps.WithLabelValues(ec.chainID.String()).Inc() + return ec.NewLegacyAttempt(etx, bumpedFee.Legacy, bumpedFeeLimit) + case 0x2: // EIP1559 ec.lggr.Debugw("Rebroadcast bumping gas for DynamicFee tx", append(logFields, "bumpedTipCap", bumpedFee.Dynamic.TipCap.String(), "bumpedFeeCap", bumpedFee.Dynamic.FeeCap.String())...) - return ec.NewDynamicFeeAttempt(etx, *bumpedFee.Dynamic, bumpedGasLimit) + if bumpedFee.Dynamic == nil { + err = errors.Errorf("Attempt %v is a type 2 transaction but estimator did not return dynamic fee bump", previousAttempt.ID) + } else { + return ec.NewDynamicFeeAttempt(etx, *bumpedFee.Dynamic, bumpedFeeLimit) + } + default: + err = errors.Errorf("invariant violation: Attempt %v had unrecognised transaction type %v"+ + "This is a bug! Please report to https://github.com/smartcontractkit/chainlink/issues", previousAttempt.ID, previousAttempt.TxType) } - default: - err = errors.Errorf("invariant violation: Attempt %v had unrecognised transaction type %v"+ - "This is a bug! Please report to https://github.com/smartcontractkit/chainlink/issues", previousAttempt.ID, previousAttempt.TxType) } if errors.Is(errors.Cause(err), gas.ErrBumpGasExceedsLimit) { diff --git a/core/chains/evm/txmgr/models.go b/core/chains/evm/txmgr/models.go index 2b4617c1f05..0ab48d828e3 100644 --- a/core/chains/evm/txmgr/models.go +++ b/core/chains/evm/txmgr/models.go @@ -311,6 +311,17 @@ func (a EthTxAttempt) GetSignedTx() (*types.Transaction, error) { return signedTx, nil } +func (a EthTxAttempt) Fee() (fee gas.EvmFee) { + fee.Legacy = a.GetGasPrice() + + dynamic := a.DynamicFee() + // add dynamic struct only if values are not nil + if dynamic.FeeCap != nil && dynamic.TipCap != nil { + fee.Dynamic = &dynamic + } + return fee +} + func (a EthTxAttempt) DynamicFee() gas.DynamicFee { return gas.DynamicFee{ FeeCap: a.GasFeeCap, From e32bfecec346565a188f58d6917cd11afc7405cf Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:23:42 -0700 Subject: [PATCH 06/10] test coverage for WrappedEvmEstimator --- core/chains/evm/gas/models_test.go | 91 +++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/core/chains/evm/gas/models_test.go b/core/chains/evm/gas/models_test.go index 0a836004d03..50bc085197a 100644 --- a/core/chains/evm/gas/models_test.go +++ b/core/chains/evm/gas/models_test.go @@ -1 +1,90 @@ -package gas +package gas_test + +import ( + "context" + "testing" + + "github.com/smartcontractkit/chainlink/core/assets" + "github.com/smartcontractkit/chainlink/core/chains/evm/gas" + "github.com/smartcontractkit/chainlink/core/chains/evm/gas/mocks" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestWrappedEvmEstimator(t *testing.T) { + ctx := context.Background() + + // fee values + gasLimit := uint32(10) + legacyFee := assets.NewWeiI(10) + dynamicFee := gas.DynamicFee{ + FeeCap: assets.NewWeiI(20), + TipCap: assets.NewWeiI(21), + } + + cfg := mocks.NewConfig(t) + e := mocks.NewEvmEstimator(t) + e.On("GetDynamicFee", mock.Anything, mock.Anything, mock.Anything). + Return(dynamicFee, gasLimit, nil).Once() + e.On("GetLegacyGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(legacyFee, gasLimit, nil).Once() + e.On("BumpDynamicFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(dynamicFee, gasLimit, nil).Once() + e.On("BumpLegacyGas", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(legacyFee, gasLimit, nil).Once() + + // GetFee returns gas estimation based on configuration value + t.Run("GetFee", func(t *testing.T) { + // expect legacy fee data + cfg.On("EvmEIP1559DynamicFees").Return(false).Once() + estimator := gas.NewWrappedEvmEstimator(e, cfg) + fee, max, err := estimator.GetFee(ctx, nil, 0, nil) + require.NoError(t, err) + assert.Equal(t, gasLimit, max) + assert.True(t, legacyFee.Equal(fee.Legacy)) + assert.Nil(t, fee.Dynamic) + + // expect dynamic fee data + cfg.On("EvmEIP1559DynamicFees").Return(true).Once() + estimator = gas.NewWrappedEvmEstimator(e, cfg) + fee, max, err = estimator.GetFee(ctx, nil, 0, nil) + require.NoError(t, err) + assert.Equal(t, gasLimit, max) + assert.True(t, dynamicFee.FeeCap.Equal(fee.Dynamic.FeeCap)) + assert.True(t, dynamicFee.TipCap.Equal(fee.Dynamic.TipCap)) + assert.Nil(t, fee.Legacy) + }) + + // BumpFee returns bumped fee type based on original fee calculation + t.Run("BumpFee", func(t *testing.T) { + cfg.On("EvmEIP1559DynamicFees").Return(false).Once().Maybe() + estimator := gas.NewWrappedEvmEstimator(e, cfg) + + // expect legacy fee data + fee, max, err := estimator.BumpFee(ctx, gas.EvmFee{Legacy: assets.NewWeiI(0)}, 0, nil, nil) + require.NoError(t, err) + assert.Equal(t, gasLimit, max) + assert.True(t, legacyFee.Equal(fee.Legacy)) + assert.Nil(t, fee.Dynamic) + + // expect dynamic fee data + var d gas.DynamicFee + fee, max, err = estimator.BumpFee(ctx, gas.EvmFee{Dynamic: &d}, 0, nil, nil) + require.NoError(t, err) + assert.Equal(t, gasLimit, max) + assert.True(t, dynamicFee.FeeCap.Equal(fee.Dynamic.FeeCap)) + assert.True(t, dynamicFee.TipCap.Equal(fee.Dynamic.TipCap)) + assert.Nil(t, fee.Legacy) + + // expect error + _, _, err = estimator.BumpFee(ctx, gas.EvmFee{}, 0, nil, nil) + assert.Error(t, err) + _, _, err = estimator.BumpFee(ctx, gas.EvmFee{ + Legacy: legacyFee, + Dynamic: &dynamicFee, + }, 0, nil, nil) + assert.Error(t, err) + }) +} \ No newline at end of file From 69ce9a24fdf1f8fc2cfbd3a687142acdad783efb Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Wed, 1 Mar 2023 09:14:02 -0700 Subject: [PATCH 07/10] generic FeeEstimator interface --- core/chains/evm/gas/mocks/fee_estimator.go | 69 +++++++++---------- core/chains/evm/gas/models.go | 30 ++++---- core/chains/evm/txmgr/eth_broadcaster.go | 5 +- core/chains/evm/txmgr/eth_broadcaster_test.go | 3 +- core/chains/evm/txmgr/eth_confirmer.go | 4 +- core/chains/evm/txmgr/mocks/tx_manager.go | 8 +-- core/chains/evm/txmgr/txmgr.go | 18 ++--- core/services/keeper/upkeep_executer.go | 4 +- core/services/keeper/upkeep_executer_test.go | 6 +- .../reasonable_gas_price_provider.go | 5 +- 10 files changed, 77 insertions(+), 75 deletions(-) diff --git a/core/chains/evm/gas/mocks/fee_estimator.go b/core/chains/evm/gas/mocks/fee_estimator.go index 705bbdb632a..4f40f3c2222 100644 --- a/core/chains/evm/gas/mocks/fee_estimator.go +++ b/core/chains/evm/gas/mocks/fee_estimator.go @@ -5,44 +5,39 @@ package mocks import ( context "context" - assets "github.com/smartcontractkit/chainlink/core/assets" - gas "github.com/smartcontractkit/chainlink/core/chains/evm/gas" - mock "github.com/stretchr/testify/mock" - - types "github.com/smartcontractkit/chainlink/core/chains/evm/types" ) // FeeEstimator is an autogenerated mock type for the FeeEstimator type -type FeeEstimator struct { +type FeeEstimator[HEAD interface{}, FEE interface{}, MAXPRICE interface{}] struct { mock.Mock } -// BumpFee provides a mock function with given fields: ctx, originalFee, gasLimit, maxGasPriceWei, attempts -func (_m *FeeEstimator) BumpFee(ctx context.Context, originalFee gas.EvmFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.PriorAttempt) (gas.EvmFee, uint32, error) { - ret := _m.Called(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) +// BumpFee provides a mock function with given fields: ctx, originalFee, feeLimit, maxFeePrice, attempts +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []gas.PriorAttempt) (FEE, uint32, error) { + ret := _m.Called(ctx, originalFee, feeLimit, maxFeePrice, attempts) - var r0 gas.EvmFee + var r0 FEE var r1 uint32 var r2 error - if rf, ok := ret.Get(0).(func(context.Context, gas.EvmFee, uint32, *assets.Wei, []gas.PriorAttempt) (gas.EvmFee, uint32, error)); ok { - return rf(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt) (FEE, uint32, error)); ok { + return rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } - if rf, ok := ret.Get(0).(func(context.Context, gas.EvmFee, uint32, *assets.Wei, []gas.PriorAttempt) gas.EvmFee); ok { - r0 = rf(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt) FEE); ok { + r0 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { - r0 = ret.Get(0).(gas.EvmFee) + r0 = ret.Get(0).(FEE) } - if rf, ok := ret.Get(1).(func(context.Context, gas.EvmFee, uint32, *assets.Wei, []gas.PriorAttempt) uint32); ok { - r1 = rf(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + if rf, ok := ret.Get(1).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt) uint32); ok { + r1 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { r1 = ret.Get(1).(uint32) } - if rf, ok := ret.Get(2).(func(context.Context, gas.EvmFee, uint32, *assets.Wei, []gas.PriorAttempt) error); ok { - r2 = rf(ctx, originalFee, gasLimit, maxGasPriceWei, attempts) + if rf, ok := ret.Get(2).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt) error); ok { + r2 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { r2 = ret.Error(2) } @@ -51,7 +46,7 @@ func (_m *FeeEstimator) BumpFee(ctx context.Context, originalFee gas.EvmFee, gas } // Close provides a mock function with given fields: -func (_m *FeeEstimator) Close() error { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) Close() error { ret := _m.Called() var r0 error @@ -64,37 +59,37 @@ func (_m *FeeEstimator) Close() error { return r0 } -// GetFee provides a mock function with given fields: ctx, calldata, gasLimit, maxGasPriceWei, opts -func (_m *FeeEstimator) GetFee(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...gas.Opt) (gas.EvmFee, uint32, error) { +// GetFee provides a mock function with given fields: ctx, calldata, feeLimit, maxFeePrice, opts +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...gas.Opt) (FEE, uint32, error) { _va := make([]interface{}, len(opts)) for _i := range opts { _va[_i] = opts[_i] } var _ca []interface{} - _ca = append(_ca, ctx, calldata, gasLimit, maxGasPriceWei) + _ca = append(_ca, ctx, calldata, feeLimit, maxFeePrice) _ca = append(_ca, _va...) ret := _m.Called(_ca...) - var r0 gas.EvmFee + var r0 FEE var r1 uint32 var r2 error - if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) (gas.EvmFee, uint32, error)); ok { - return rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, MAXPRICE, ...gas.Opt) (FEE, uint32, error)); ok { + return rf(ctx, calldata, feeLimit, maxFeePrice, opts...) } - if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) gas.EvmFee); ok { - r0 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, MAXPRICE, ...gas.Opt) FEE); ok { + r0 = rf(ctx, calldata, feeLimit, maxFeePrice, opts...) } else { - r0 = ret.Get(0).(gas.EvmFee) + r0 = ret.Get(0).(FEE) } - if rf, ok := ret.Get(1).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) uint32); ok { - r1 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + if rf, ok := ret.Get(1).(func(context.Context, []byte, uint32, MAXPRICE, ...gas.Opt) uint32); ok { + r1 = rf(ctx, calldata, feeLimit, maxFeePrice, opts...) } else { r1 = ret.Get(1).(uint32) } - if rf, ok := ret.Get(2).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) error); ok { - r2 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + if rf, ok := ret.Get(2).(func(context.Context, []byte, uint32, MAXPRICE, ...gas.Opt) error); ok { + r2 = rf(ctx, calldata, feeLimit, maxFeePrice, opts...) } else { r2 = ret.Error(2) } @@ -103,12 +98,12 @@ func (_m *FeeEstimator) GetFee(ctx context.Context, calldata []byte, gasLimit ui } // OnNewLongestChain provides a mock function with given fields: _a0, _a1 -func (_m *FeeEstimator) OnNewLongestChain(_a0 context.Context, _a1 *types.Head) { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) OnNewLongestChain(_a0 context.Context, _a1 HEAD) { _m.Called(_a0, _a1) } // Start provides a mock function with given fields: _a0 -func (_m *FeeEstimator) Start(_a0 context.Context) error { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) Start(_a0 context.Context) error { ret := _m.Called(_a0) var r0 error @@ -127,8 +122,8 @@ type mockConstructorTestingTNewFeeEstimator interface { } // NewFeeEstimator creates a new instance of FeeEstimator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewFeeEstimator(t mockConstructorTestingTNewFeeEstimator) *FeeEstimator { - mock := &FeeEstimator{} +func NewFeeEstimator[HEAD interface{}, FEE interface{}, MAXPRICE interface{}](t mockConstructorTestingTNewFeeEstimator) *FeeEstimator[HEAD, FEE, MAXPRICE] { + mock := &FeeEstimator[HEAD, FEE, MAXPRICE]{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index ebbc46c138d..072e94b1cad 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -28,7 +28,7 @@ func IsBumpErr(err error) bool { } // NewEstimator returns the estimator for a given config -func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) FeeEstimator { +func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei] { s := cfg.GasEstimatorMode() lggr.Infow(fmt.Sprintf("Initializing EVM gas estimator in mode: %s", s), "estimatorMode", s, @@ -111,47 +111,49 @@ type EvmFee struct { Dynamic *DynamicFee } -// Estimator provides an interface that wraps the EVM specific dynamic and legacy estimators into one estimator +// FeeEstimator provides a generic interface for fee estimation // //go:generate mockery --quiet --name FeeEstimator --output ./mocks/ --case=underscore -type FeeEstimator interface { - OnNewLongestChain(context.Context, *evmtypes.Head) +type FeeEstimator[HEAD any, FEE any, MAXPRICE any] interface { + OnNewLongestChain(context.Context, HEAD) Start(context.Context) error Close() error - GetFee(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...Opt) (fee EvmFee, chainSpecificGasLimit uint32, err error) - BumpFee(ctx context.Context, originalFee EvmFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumpedFee EvmFee, chainSpecificGasLimit uint32, err error) + GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...Opt) (fee FEE, chainSpecificFeeLimit uint32, err error) + BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []PriorAttempt) (bumpedFee FEE, chainSpecificFeeLimit uint32, err error) } +// WrappedEvmEstimator provides a struct that wraps the EVM specific dynamic and legacy estimators into one estimator that conforms to the generic FeeEstimator type WrappedEvmEstimator struct { EvmEstimator EIP1559Enabled bool } -var _ FeeEstimator = (*WrappedEvmEstimator)(nil) +// var _ FeeEstimator = (*WrappedEvmEstimator)(nil) +var _ FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei] = (*WrappedEvmEstimator)(nil) -func NewWrappedEvmEstimator(e EvmEstimator, cfg Config) FeeEstimator { +func NewWrappedEvmEstimator(e EvmEstimator, cfg Config) FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei] { return &WrappedEvmEstimator{ EvmEstimator: e, EIP1559Enabled: cfg.EvmEIP1559DynamicFees(), } } -func (e WrappedEvmEstimator) GetFee(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...Opt) (fee EvmFee, chainSpecificGasLimit uint32, err error) { +func (e WrappedEvmEstimator) GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice *assets.Wei, opts ...Opt) (fee EvmFee, chainSpecificFeeLimit uint32, err error) { // get dynamic fee if e.EIP1559Enabled { var dynamicFee DynamicFee - dynamicFee, chainSpecificGasLimit, err = e.EvmEstimator.GetDynamicFee(ctx, gasLimit, maxGasPriceWei) + dynamicFee, chainSpecificFeeLimit, err = e.EvmEstimator.GetDynamicFee(ctx, feeLimit, maxFeePrice) fee.Dynamic = &dynamicFee return } // get legacy fee - fee.Legacy, chainSpecificGasLimit, err = e.EvmEstimator.GetLegacyGas(ctx, calldata, gasLimit, maxGasPriceWei, opts...) + fee.Legacy, chainSpecificFeeLimit, err = e.EvmEstimator.GetLegacyGas(ctx, calldata, feeLimit, maxFeePrice, opts...) return } -func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumpedFee EvmFee, chainSpecificGasLimit uint32, err error) { +func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, feeLimit uint32, maxFeePrice *assets.Wei, attempts []PriorAttempt) (bumpedFee EvmFee, chainSpecificFeeLimit uint32, err error) { // validate only 1 fee type is present if (originalFee.Dynamic == nil && originalFee.Legacy == nil) || (originalFee.Dynamic != nil && originalFee.Legacy != nil) { err = errors.New("only one dynamic or legacy fee can be defined") @@ -162,13 +164,13 @@ func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, ga // bump dynamic original if originalFee.Dynamic != nil { var bumpedDynamic DynamicFee - bumpedDynamic, chainSpecificGasLimit, err = e.EvmEstimator.BumpDynamicFee(ctx, *originalFee.Dynamic, gasLimit, maxGasPriceWei, attempts) + bumpedDynamic, chainSpecificFeeLimit, err = e.EvmEstimator.BumpDynamicFee(ctx, *originalFee.Dynamic, feeLimit, maxFeePrice, attempts) bumpedFee.Dynamic = &bumpedDynamic return } // bump legacy fee - bumpedFee.Legacy, chainSpecificGasLimit, err = e.EvmEstimator.BumpLegacyGas(ctx, originalFee.Legacy, gasLimit, maxGasPriceWei, attempts) + bumpedFee.Legacy, chainSpecificFeeLimit, err = e.EvmEstimator.BumpLegacyGas(ctx, originalFee.Legacy, feeLimit, maxFeePrice, attempts) return } diff --git a/core/chains/evm/txmgr/eth_broadcaster.go b/core/chains/evm/txmgr/eth_broadcaster.go index 2f22a0ba647..256be8374bb 100644 --- a/core/chains/evm/txmgr/eth_broadcaster.go +++ b/core/chains/evm/txmgr/eth_broadcaster.go @@ -24,6 +24,7 @@ import ( evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/core/chains/evm/label" + evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" "github.com/smartcontractkit/chainlink/core/logger" "github.com/smartcontractkit/chainlink/core/services/keystore/keys/ethkey" "github.com/smartcontractkit/chainlink/core/services/pg" @@ -95,7 +96,7 @@ type EthBroadcaster struct { q pg.Q ethClient evmclient.Client ChainKeyStore - estimator gas.FeeEstimator + estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] resumeCallback ResumeCallback // autoSyncNonce, if set, will cause EthBroadcaster to fast-forward the nonce @@ -123,7 +124,7 @@ type EthBroadcaster struct { // NewEthBroadcaster returns a new concrete EthBroadcaster func NewEthBroadcaster(db *sqlx.DB, ethClient evmclient.Client, config Config, keystore KeyStore, eventBroadcaster pg.EventBroadcaster, - keyStates []ethkey.State, estimator gas.FeeEstimator, resumeCallback ResumeCallback, + keyStates []ethkey.State, estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], resumeCallback ResumeCallback, logger logger.Logger, checkerFactory TransmitCheckerFactory, autoSyncNonce bool) *EthBroadcaster { triggers := make(map[gethCommon.Address]chan struct{}) diff --git a/core/chains/evm/txmgr/eth_broadcaster_test.go b/core/chains/evm/txmgr/eth_broadcaster_test.go index ac6b7c21af3..240f54198a3 100644 --- a/core/chains/evm/txmgr/eth_broadcaster_test.go +++ b/core/chains/evm/txmgr/eth_broadcaster_test.go @@ -28,6 +28,7 @@ import ( "github.com/smartcontractkit/chainlink/core/chains/evm/gas" gasmocks "github.com/smartcontractkit/chainlink/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/core/chains/evm/txmgr" + evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" "github.com/smartcontractkit/chainlink/core/internal/cltest" "github.com/smartcontractkit/chainlink/core/internal/cltest/heavyweight" "github.com/smartcontractkit/chainlink/core/internal/testutils" @@ -576,7 +577,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi chStartEstimate := make(chan struct{}) chBlock := make(chan struct{}) - estimator := gasmocks.NewFeeEstimator(t) + estimator := gasmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei](t) estimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, evmcfg.KeySpecificMaxGasPriceWei(fromAddress)).Return(gas.EvmFee{Legacy: assets.GWei(32)}, uint32(500), nil).Run(func(_ mock.Arguments) { close(chStartEstimate) <-chBlock diff --git a/core/chains/evm/txmgr/eth_confirmer.go b/core/chains/evm/txmgr/eth_confirmer.go index 922b5db84c8..48dee5cb537 100644 --- a/core/chains/evm/txmgr/eth_confirmer.go +++ b/core/chains/evm/txmgr/eth_confirmer.go @@ -117,7 +117,7 @@ type EthConfirmer struct { lggr logger.Logger ethClient evmclient.Client ChainKeyStore - estimator gas.FeeEstimator + estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] resumeCallback ResumeCallback keyStates []ethkey.State @@ -132,7 +132,7 @@ type EthConfirmer struct { // NewEthConfirmer instantiates a new eth confirmer func NewEthConfirmer(orm ORM, ethClient evmclient.Client, config Config, keystore KeyStore, - keyStates []ethkey.State, estimator gas.FeeEstimator, resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { + keyStates []ethkey.State, estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { ctx, cancel := context.WithCancel(context.Background()) lggr = lggr.Named("EthConfirmer") diff --git a/core/chains/evm/txmgr/mocks/tx_manager.go b/core/chains/evm/txmgr/mocks/tx_manager.go index 2ce09fea678..b32417e684a 100644 --- a/core/chains/evm/txmgr/mocks/tx_manager.go +++ b/core/chains/evm/txmgr/mocks/tx_manager.go @@ -99,15 +99,15 @@ func (_m *TxManager) GetForwarderForEOA(eoa common.Address) (common.Address, err } // GetGasEstimator provides a mock function with given fields: -func (_m *TxManager) GetGasEstimator() gas.FeeEstimator { +func (_m *TxManager) GetGasEstimator() gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei] { ret := _m.Called() - var r0 gas.FeeEstimator - if rf, ok := ret.Get(0).(func() gas.FeeEstimator); ok { + var r0 gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei] + if rf, ok := ret.Get(0).(func() gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei]); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(gas.FeeEstimator) + r0 = ret.Get(0).(gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei]) } } diff --git a/core/chains/evm/txmgr/txmgr.go b/core/chains/evm/txmgr/txmgr.go index a1dc7a0570c..530dfe67021 100644 --- a/core/chains/evm/txmgr/txmgr.go +++ b/core/chains/evm/txmgr/txmgr.go @@ -79,7 +79,7 @@ type TxManager interface { Trigger(addr common.Address) CreateEthTransaction(newTx NewTx, qopts ...pg.QOpt) (etx EthTx, err error) GetForwarderForEOA(eoa common.Address) (forwarder common.Address, err error) - GetGasEstimator() gas.FeeEstimator + GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] RegisterResumeCallback(fn ResumeCallback) SendEther(chainID *big.Int, from, to common.Address, value assets.Eth, gasLimit uint32) (etx EthTx, err error) Reset(f func(), addr common.Address, abandon bool) error @@ -104,7 +104,7 @@ type Txm struct { config Config keyStore KeyStore eventBroadcaster pg.EventBroadcaster - gasEstimator gas.FeeEstimator + gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] chainID big.Int checkerFactory TransmitCheckerFactory @@ -556,7 +556,7 @@ func (b *Txm) checkEnabled(addr common.Address) error { } // GetGasEstimator returns the gas estimator, mostly useful for tests -func (b *Txm) GetGasEstimator() gas.FeeEstimator { +func (b *Txm) GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] { return b.gasEstimator } @@ -724,9 +724,11 @@ func (n *NullTxManager) Reset(f func(), addr common.Address, abandon bool) error func (n *NullTxManager) SendEther(chainID *big.Int, from, to common.Address, value assets.Eth, gasLimit uint32) (etx EthTx, err error) { return etx, errors.New(n.ErrMsg) } -func (n *NullTxManager) Healthy() error { return nil } -func (n *NullTxManager) Ready() error { return nil } -func (n *NullTxManager) Name() string { return "" } -func (n *NullTxManager) HealthReport() map[string]error { return nil } -func (n *NullTxManager) GetGasEstimator() gas.FeeEstimator { return nil } +func (n *NullTxManager) Healthy() error { return nil } +func (n *NullTxManager) Ready() error { return nil } +func (n *NullTxManager) Name() string { return "" } +func (n *NullTxManager) HealthReport() map[string]error { return nil } +func (n *NullTxManager) GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] { + return nil +} func (n *NullTxManager) RegisterResumeCallback(fn ResumeCallback) {} diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index 5a78f63cfde..9135d4e15a4 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -51,7 +51,7 @@ type UpkeepExecuter struct { config Config executionQueue chan struct{} headBroadcaster httypes.HeadBroadcasterRegistry - gasEstimator gas.FeeEstimator + gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] job job.Job mailbox *utils.Mailbox[*evmtypes.Head] orm ORM @@ -69,7 +69,7 @@ func NewUpkeepExecuter( pr pipeline.Runner, ethClient evmclient.Client, headBroadcaster httypes.HeadBroadcaster, - gasEstimator gas.FeeEstimator, + gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], logger logger.Logger, config Config, effectiveKeeperAddress common.Address, diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index 4e2d680d800..a590f78dbba 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -40,10 +40,10 @@ func newHead() evmtypes.Head { return evmtypes.NewHead(big.NewInt(20), utils.NewHash(), utils.NewHash(), 1000, utils.NewBigI(0)) } -func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator) { +func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei]) { // note: estimator will only return 1 of legacy or dynamic fees (not both) // assumed to call legacy estimator only - estimator = gasmocks.NewFeeEstimator(t) + estimator = gasmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei](t) estimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe().Return(gas.EvmFee{ Legacy: assets.GWei(60), Dynamic: nil, @@ -51,7 +51,7 @@ func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator) { return } -func setup(t *testing.T, estimator *gasmocks.FeeEstimator, overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) ( +func setup(t *testing.T, estimator *gasmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) ( *sqlx.DB, config.GeneralConfig, *evmmocks.Client, diff --git a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go index 28ede14fc21..6a9818fdc66 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go +++ b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go @@ -8,11 +8,12 @@ import ( "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" + evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" ) // reasonableGasPriceProvider provides an estimate for the average gas price type reasonableGasPriceProvider struct { - estimator gas.FeeEstimator + estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] timeout time.Duration maxGasPrice *assets.Wei supportsDynamicFee bool @@ -21,7 +22,7 @@ type reasonableGasPriceProvider struct { var _ types.ReasonableGasPrice = (*reasonableGasPriceProvider)(nil) func NewReasonableGasPriceProvider( - estimator gas.FeeEstimator, + estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], timeout time.Duration, maxGasPrice *assets.Wei, supportsDynamicFee bool, From afc78591867a55abae9cedd1877a519cda764aa8 Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Wed, 1 Mar 2023 11:03:40 -0700 Subject: [PATCH 08/10] generic PriorAttempt interface --- .../chains/evm/gas/block_history_estimator.go | 6 +- .../evm/gas/block_history_estimator_test.go | 40 +++++++----- core/chains/evm/gas/fixed_price_estimator.go | 4 +- core/chains/evm/gas/helpers_test.go | 5 +- core/chains/evm/gas/l2_suggested_estimator.go | 4 +- core/chains/evm/gas/mocks/evm_estimator.go | 20 +++--- core/chains/evm/gas/mocks/fee_estimator.go | 24 +++---- core/chains/evm/gas/models.go | 65 +++++++++++++++---- core/chains/evm/txmgr/eth_broadcaster.go | 7 +- core/chains/evm/txmgr/eth_broadcaster_test.go | 3 +- core/chains/evm/txmgr/eth_confirmer.go | 7 +- core/chains/evm/txmgr/mocks/tx_manager.go | 8 +-- core/chains/evm/txmgr/models.go | 10 +-- core/chains/evm/txmgr/txmgr.go | 8 +-- core/services/keeper/upkeep_executer.go | 4 +- core/services/keeper/upkeep_executer_test.go | 6 +- .../reasonable_gas_price_provider.go | 5 +- 17 files changed, 139 insertions(+), 87 deletions(-) diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index 7c4547bf4e0..f0688a1a027 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -264,7 +264,7 @@ func (b *BlockHistoryEstimator) getTipCap() *assets.Wei { return b.tipCap } -func (b *BlockHistoryEstimator) BumpLegacyGas(_ context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { +func (b *BlockHistoryEstimator) BumpLegacyGas(_ context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []EvmPriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { if b.config.BlockHistoryEstimatorCheckInclusionBlocks() > 0 { if err = b.checkConnectivity(attempts); err != nil { if errors.Is(err, ErrConnectivity) { @@ -280,7 +280,7 @@ func (b *BlockHistoryEstimator) BumpLegacyGas(_ context.Context, originalGasPric // checkConnectivity detects if the transaction is not being included due to // some kind of mempool propagation or connectivity issue rather than // insufficiently high pricing and returns error if so -func (b *BlockHistoryEstimator) checkConnectivity(attempts []PriorAttempt) error { +func (b *BlockHistoryEstimator) checkConnectivity(attempts []EvmPriorAttempt) error { percentile := int(b.config.BlockHistoryEstimatorCheckInclusionPercentile()) // how many blocks since broadcast? latestBlockNum := b.getCurrentBlockNum() @@ -438,7 +438,7 @@ func calcFeeCap(latestAvailableBaseFeePerGas *assets.Wei, cfg Config, tipCap *as return feeCap } -func (b *BlockHistoryEstimator) BumpDynamicFee(_ context.Context, originalFee DynamicFee, originalGasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { +func (b *BlockHistoryEstimator) BumpDynamicFee(_ context.Context, originalFee DynamicFee, originalGasLimit uint32, maxGasPriceWei *assets.Wei, attempts []EvmPriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { if b.config.BlockHistoryEstimatorCheckInclusionBlocks() > 0 { if err = b.checkConnectivity(attempts); err != nil { if errors.Is(err, ErrConnectivity) { diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index 1f96b59a0bc..6685e5b8cf8 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -1806,7 +1806,7 @@ func TestBlockHistoryEstimator_GetDynamicFee(t *testing.T) { }) } -var _ gas.PriorAttempt = &MockAttempt{} +var _ gas.PriorAttempt[gas.EvmFee, common.Hash] = &MockAttempt{} type MockAttempt struct { BroadcastBeforeBlockNum *int64 @@ -1817,11 +1817,21 @@ type MockAttempt struct { GasTipCap *assets.Wei } -func (m *MockAttempt) GetGasPrice() *assets.Wei { +func (m *MockAttempt) Fee() (f gas.EvmFee) { + f.Legacy = m.getGasPrice() + + d := m.dynamicFee() + if d.FeeCap != nil && d.TipCap != nil { + f.Dynamic = &d + } + return f +} + +func (m *MockAttempt) getGasPrice() *assets.Wei { return m.GasPrice } -func (m *MockAttempt) DynamicFee() gas.DynamicFee { +func (m *MockAttempt) dynamicFee() gas.DynamicFee { return gas.DynamicFee{ FeeCap: m.GasFeeCap, TipCap: m.GasTipCap, @@ -1853,7 +1863,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { gas.NewBlockHistoryEstimator(lggr, nil, cfg, *testutils.NewRandomEVMChainID()), ) - attempts := []gas.PriorAttempt{ + attempts := []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x0, Hash: utils.NewHash()}, } @@ -1918,7 +1928,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { num := int64(0) hash := utils.NewHash() - attempts = []gas.PriorAttempt{ + attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x3, BroadcastBeforeBlockNum: &num, Hash: hash}, } @@ -1928,7 +1938,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { assert.Contains(t, err.Error(), fmt.Sprintf("attempt %s has unknown transaction type 0x3", hash)) }) - attempts = []gas.PriorAttempt{ + attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x0, BroadcastBeforeBlockNum: &num, Hash: hash}, } @@ -1965,7 +1975,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { } gas.SetRollingBlockHistory(bhe, []evmtypes.Block{b0, b1, b2, b3}) - attempts = []gas.PriorAttempt{ + attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(1000), BroadcastBeforeBlockNum: testutils.Ptr(int64(4))}, // This is very expensive but will be ignored due to BroadcastBeforeBlockNum being too recent &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(3), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(5), BroadcastBeforeBlockNum: testutils.Ptr(int64(1))}, @@ -2014,7 +2024,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { } gas.SetRollingBlockHistory(bhe, []evmtypes.Block{b0}) - attempts = []gas.PriorAttempt{ + attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(1), GasTipCap: assets.NewWeiI(3), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(10), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, } @@ -2035,7 +2045,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { assert.Contains(t, err.Error(), fmt.Sprintf("transaction %s has gas price of 10 wei, which is above percentile=60%% (percentile price: 7 wei) for blocks 3 thru 3 (checking 1 blocks)", attempts[1].GetHash())) }) - attempts = []gas.PriorAttempt{ + attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(11), GasTipCap: assets.NewWeiI(10), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(3), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, } @@ -2083,7 +2093,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { blocks := []evmtypes.Block{b0, b1, b2, b3} gas.SetRollingBlockHistory(bhe, blocks) - attempts = []gas.PriorAttempt{ + attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(30), GasTipCap: assets.NewWeiI(1000), BroadcastBeforeBlockNum: testutils.Ptr(int64(4))}, // This is very expensive but will be ignored due to BroadcastBeforeBlockNum being too recent &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(30), GasTipCap: assets.NewWeiI(3), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(30), GasTipCap: assets.NewWeiI(5), BroadcastBeforeBlockNum: testutils.Ptr(int64(1))}, @@ -2118,7 +2128,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { cfg.BlockHistoryEstimatorCheckInclusionBlocksF = 3 cfg.BlockHistoryEstimatorCheckInclusionPercentileF = 5 - attempts = []gas.PriorAttempt{ + attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(4), GasTipCap: assets.NewWeiI(7), BroadcastBeforeBlockNum: testutils.Ptr(int64(1))}, } @@ -2151,11 +2161,11 @@ func TestBlockHistoryEstimator_Bumps(t *testing.T) { head := cltest.Head(1) bhe.OnNewLongestChain(testutils.Context(t), head) - attempts := []gas.PriorAttempt{ + attempts := []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(1000), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, } - _, _, err := bhe.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), 100000, maxGasPrice, attempts) + _, _, err := bhe.BumpLegacyGas(testutils.Context(t), assets.NewWeiI(42), 100000, maxGasPrice, gas.MakeEvmPriorAttempts(attempts)) require.Error(t, err) assert.True(t, errors.Is(err, gas.ErrConnectivity)) assert.Contains(t, err.Error(), fmt.Sprintf("transaction %s has gas price of 1 kwei, which is above percentile=10%% (percentile price: 1 wei) for blocks 1 thru 1 (checking 1 blocks)", attempts[0].GetHash())) @@ -2258,11 +2268,11 @@ func TestBlockHistoryEstimator_Bumps(t *testing.T) { bhe.OnNewLongestChain(testutils.Context(t), head) originalFee := gas.DynamicFee{FeeCap: assets.NewWeiI(100), TipCap: assets.NewWeiI(25)} - attempts := []gas.PriorAttempt{ + attempts := []gas.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasTipCap: originalFee.TipCap, GasFeeCap: originalFee.FeeCap, BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, } - _, _, err := bhe.BumpDynamicFee(testutils.Context(t), originalFee, 100000, maxGasPrice, attempts) + _, _, err := bhe.BumpDynamicFee(testutils.Context(t), originalFee, 100000, maxGasPrice, gas.MakeEvmPriorAttempts(attempts)) require.Error(t, err) assert.True(t, errors.Is(err, gas.ErrConnectivity)) assert.Contains(t, err.Error(), fmt.Sprintf("transaction %s has tip cap of 25 wei, which is above percentile=10%% (percentile tip cap: 1 wei) for blocks 1 thru 1 (checking 1 blocks)", attempts[0].GetHash())) diff --git a/core/chains/evm/gas/fixed_price_estimator.go b/core/chains/evm/gas/fixed_price_estimator.go index 71efcbc88b8..823d1b457c9 100644 --- a/core/chains/evm/gas/fixed_price_estimator.go +++ b/core/chains/evm/gas/fixed_price_estimator.go @@ -42,7 +42,7 @@ func (f *fixedPriceEstimator) GetLegacyGas(_ context.Context, _ []byte, gasLimit return } -func (f *fixedPriceEstimator) BumpLegacyGas(_ context.Context, originalGasPrice *assets.Wei, originalGasLimit uint32, maxGasPriceWei *assets.Wei, _ []PriorAttempt) (gasPrice *assets.Wei, gasLimit uint32, err error) { +func (f *fixedPriceEstimator) BumpLegacyGas(_ context.Context, originalGasPrice *assets.Wei, originalGasLimit uint32, maxGasPriceWei *assets.Wei, _ []EvmPriorAttempt) (gasPrice *assets.Wei, gasLimit uint32, err error) { return BumpLegacyGasPriceOnly(f.config, f.lggr, f.config.EvmGasPriceDefault(), originalGasPrice, originalGasLimit, maxGasPriceWei) } @@ -68,6 +68,6 @@ func (f *fixedPriceEstimator) GetDynamicFee(_ context.Context, originalGasLimit }, chainSpecificGasLimit, nil } -func (f *fixedPriceEstimator) BumpDynamicFee(_ context.Context, originalFee DynamicFee, originalGasLimit uint32, maxGasPriceWei *assets.Wei, _ []PriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { +func (f *fixedPriceEstimator) BumpDynamicFee(_ context.Context, originalFee DynamicFee, originalGasLimit uint32, maxGasPriceWei *assets.Wei, _ []EvmPriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { return BumpDynamicFeeOnly(f.config, f.lggr, f.config.EvmGasTipCapDefault(), nil, originalFee, originalGasLimit, maxGasPriceWei) } diff --git a/core/chains/evm/gas/helpers_test.go b/core/chains/evm/gas/helpers_test.go index 2a4250c4c58..b9acdcb4c08 100644 --- a/core/chains/evm/gas/helpers_test.go +++ b/core/chains/evm/gas/helpers_test.go @@ -4,6 +4,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink/core/assets" @@ -16,8 +17,8 @@ func init() { MaxStartTime = 1 * time.Second } -func (b *BlockHistoryEstimator) CheckConnectivity(attempts []PriorAttempt) error { - return b.checkConnectivity(attempts) +func (b *BlockHistoryEstimator) CheckConnectivity(attempts []PriorAttempt[EvmFee, common.Hash]) error { + return b.checkConnectivity(MakeEvmPriorAttempts(attempts)) } func BlockHistoryEstimatorFromInterface(bhe EvmEstimator) *BlockHistoryEstimator { diff --git a/core/chains/evm/gas/l2_suggested_estimator.go b/core/chains/evm/gas/l2_suggested_estimator.go index 5f7986443c4..47aed66fb0c 100644 --- a/core/chains/evm/gas/l2_suggested_estimator.go +++ b/core/chains/evm/gas/l2_suggested_estimator.go @@ -118,7 +118,7 @@ func (*l2SuggestedPriceEstimator) GetDynamicFee(_ context.Context, _ uint32, _ * return } -func (*l2SuggestedPriceEstimator) BumpDynamicFee(_ context.Context, _ DynamicFee, _ uint32, _ *assets.Wei, _ []PriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { +func (*l2SuggestedPriceEstimator) BumpDynamicFee(_ context.Context, _ DynamicFee, _ uint32, _ *assets.Wei, _ []EvmPriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) { err = errors.New("dynamic fees are not implemented for this layer 2") return } @@ -166,7 +166,7 @@ func (o *l2SuggestedPriceEstimator) GetLegacyGas(ctx context.Context, _ []byte, return } -func (o *l2SuggestedPriceEstimator) BumpLegacyGas(_ context.Context, _ *assets.Wei, _ uint32, _ *assets.Wei, _ []PriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { +func (o *l2SuggestedPriceEstimator) BumpLegacyGas(_ context.Context, _ *assets.Wei, _ uint32, _ *assets.Wei, _ []EvmPriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { return nil, 0, errors.New("bump gas is not supported for this l2") } diff --git a/core/chains/evm/gas/mocks/evm_estimator.go b/core/chains/evm/gas/mocks/evm_estimator.go index 96a9a5cf758..de1cf0a9312 100644 --- a/core/chains/evm/gas/mocks/evm_estimator.go +++ b/core/chains/evm/gas/mocks/evm_estimator.go @@ -20,28 +20,28 @@ type EvmEstimator struct { } // BumpDynamicFee provides a mock function with given fields: ctx, original, gasLimit, maxGasPriceWei, attempts -func (_m *EvmEstimator) BumpDynamicFee(ctx context.Context, original gas.DynamicFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.PriorAttempt) (gas.DynamicFee, uint32, error) { +func (_m *EvmEstimator) BumpDynamicFee(ctx context.Context, original gas.DynamicFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.EvmPriorAttempt) (gas.DynamicFee, uint32, error) { ret := _m.Called(ctx, original, gasLimit, maxGasPriceWei, attempts) var r0 gas.DynamicFee var r1 uint32 var r2 error - if rf, ok := ret.Get(0).(func(context.Context, gas.DynamicFee, uint32, *assets.Wei, []gas.PriorAttempt) (gas.DynamicFee, uint32, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, gas.DynamicFee, uint32, *assets.Wei, []gas.EvmPriorAttempt) (gas.DynamicFee, uint32, error)); ok { return rf(ctx, original, gasLimit, maxGasPriceWei, attempts) } - if rf, ok := ret.Get(0).(func(context.Context, gas.DynamicFee, uint32, *assets.Wei, []gas.PriorAttempt) gas.DynamicFee); ok { + if rf, ok := ret.Get(0).(func(context.Context, gas.DynamicFee, uint32, *assets.Wei, []gas.EvmPriorAttempt) gas.DynamicFee); ok { r0 = rf(ctx, original, gasLimit, maxGasPriceWei, attempts) } else { r0 = ret.Get(0).(gas.DynamicFee) } - if rf, ok := ret.Get(1).(func(context.Context, gas.DynamicFee, uint32, *assets.Wei, []gas.PriorAttempt) uint32); ok { + if rf, ok := ret.Get(1).(func(context.Context, gas.DynamicFee, uint32, *assets.Wei, []gas.EvmPriorAttempt) uint32); ok { r1 = rf(ctx, original, gasLimit, maxGasPriceWei, attempts) } else { r1 = ret.Get(1).(uint32) } - if rf, ok := ret.Get(2).(func(context.Context, gas.DynamicFee, uint32, *assets.Wei, []gas.PriorAttempt) error); ok { + if rf, ok := ret.Get(2).(func(context.Context, gas.DynamicFee, uint32, *assets.Wei, []gas.EvmPriorAttempt) error); ok { r2 = rf(ctx, original, gasLimit, maxGasPriceWei, attempts) } else { r2 = ret.Error(2) @@ -51,16 +51,16 @@ func (_m *EvmEstimator) BumpDynamicFee(ctx context.Context, original gas.Dynamic } // BumpLegacyGas provides a mock function with given fields: ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts -func (_m *EvmEstimator) BumpLegacyGas(ctx context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.PriorAttempt) (*assets.Wei, uint32, error) { +func (_m *EvmEstimator) BumpLegacyGas(ctx context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []gas.EvmPriorAttempt) (*assets.Wei, uint32, error) { ret := _m.Called(ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts) var r0 *assets.Wei var r1 uint32 var r2 error - if rf, ok := ret.Get(0).(func(context.Context, *assets.Wei, uint32, *assets.Wei, []gas.PriorAttempt) (*assets.Wei, uint32, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, *assets.Wei, uint32, *assets.Wei, []gas.EvmPriorAttempt) (*assets.Wei, uint32, error)); ok { return rf(ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts) } - if rf, ok := ret.Get(0).(func(context.Context, *assets.Wei, uint32, *assets.Wei, []gas.PriorAttempt) *assets.Wei); ok { + if rf, ok := ret.Get(0).(func(context.Context, *assets.Wei, uint32, *assets.Wei, []gas.EvmPriorAttempt) *assets.Wei); ok { r0 = rf(ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts) } else { if ret.Get(0) != nil { @@ -68,13 +68,13 @@ func (_m *EvmEstimator) BumpLegacyGas(ctx context.Context, originalGasPrice *ass } } - if rf, ok := ret.Get(1).(func(context.Context, *assets.Wei, uint32, *assets.Wei, []gas.PriorAttempt) uint32); ok { + if rf, ok := ret.Get(1).(func(context.Context, *assets.Wei, uint32, *assets.Wei, []gas.EvmPriorAttempt) uint32); ok { r1 = rf(ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts) } else { r1 = ret.Get(1).(uint32) } - if rf, ok := ret.Get(2).(func(context.Context, *assets.Wei, uint32, *assets.Wei, []gas.PriorAttempt) error); ok { + if rf, ok := ret.Get(2).(func(context.Context, *assets.Wei, uint32, *assets.Wei, []gas.EvmPriorAttempt) error); ok { r2 = rf(ctx, originalGasPrice, gasLimit, maxGasPriceWei, attempts) } else { r2 = ret.Error(2) diff --git a/core/chains/evm/gas/mocks/fee_estimator.go b/core/chains/evm/gas/mocks/fee_estimator.go index 4f40f3c2222..0b6b7baa8d5 100644 --- a/core/chains/evm/gas/mocks/fee_estimator.go +++ b/core/chains/evm/gas/mocks/fee_estimator.go @@ -10,33 +10,33 @@ import ( ) // FeeEstimator is an autogenerated mock type for the FeeEstimator type -type FeeEstimator[HEAD interface{}, FEE interface{}, MAXPRICE interface{}] struct { +type FeeEstimator[HEAD interface{}, FEE interface{}, MAXPRICE interface{}, HASH interface{}] struct { mock.Mock } // BumpFee provides a mock function with given fields: ctx, originalFee, feeLimit, maxFeePrice, attempts -func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []gas.PriorAttempt) (FEE, uint32, error) { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []gas.PriorAttempt[FEE, HASH]) (FEE, uint32, error) { ret := _m.Called(ctx, originalFee, feeLimit, maxFeePrice, attempts) var r0 FEE var r1 uint32 var r2 error - if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt) (FEE, uint32, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt[FEE, HASH]) (FEE, uint32, error)); ok { return rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } - if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt) FEE); ok { + if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt[FEE, HASH]) FEE); ok { r0 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { r0 = ret.Get(0).(FEE) } - if rf, ok := ret.Get(1).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt) uint32); ok { + if rf, ok := ret.Get(1).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt[FEE, HASH]) uint32); ok { r1 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { r1 = ret.Get(1).(uint32) } - if rf, ok := ret.Get(2).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt) error); ok { + if rf, ok := ret.Get(2).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt[FEE, HASH]) error); ok { r2 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { r2 = ret.Error(2) @@ -46,7 +46,7 @@ func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) BumpFee(ctx context.Context, origin } // Close provides a mock function with given fields: -func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) Close() error { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) Close() error { ret := _m.Called() var r0 error @@ -60,7 +60,7 @@ func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) Close() error { } // GetFee provides a mock function with given fields: ctx, calldata, feeLimit, maxFeePrice, opts -func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...gas.Opt) (FEE, uint32, error) { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...gas.Opt) (FEE, uint32, error) { _va := make([]interface{}, len(opts)) for _i := range opts { _va[_i] = opts[_i] @@ -98,12 +98,12 @@ func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) GetFee(ctx context.Context, calldat } // OnNewLongestChain provides a mock function with given fields: _a0, _a1 -func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) OnNewLongestChain(_a0 context.Context, _a1 HEAD) { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) OnNewLongestChain(_a0 context.Context, _a1 HEAD) { _m.Called(_a0, _a1) } // Start provides a mock function with given fields: _a0 -func (_m *FeeEstimator[HEAD, FEE, MAXPRICE]) Start(_a0 context.Context) error { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) Start(_a0 context.Context) error { ret := _m.Called(_a0) var r0 error @@ -122,8 +122,8 @@ type mockConstructorTestingTNewFeeEstimator interface { } // NewFeeEstimator creates a new instance of FeeEstimator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewFeeEstimator[HEAD interface{}, FEE interface{}, MAXPRICE interface{}](t mockConstructorTestingTNewFeeEstimator) *FeeEstimator[HEAD, FEE, MAXPRICE] { - mock := &FeeEstimator[HEAD, FEE, MAXPRICE]{} +func NewFeeEstimator[HEAD interface{}, FEE interface{}, MAXPRICE interface{}, HASH interface{}](t mockConstructorTestingTNewFeeEstimator) *FeeEstimator[HEAD, FEE, MAXPRICE, HASH] { + mock := &FeeEstimator[HEAD, FEE, MAXPRICE, HASH]{} mock.Mock.Test(t) t.Cleanup(func() { mock.AssertExpectations(t) }) diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index 072e94b1cad..f3a13480431 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -28,7 +28,7 @@ func IsBumpErr(err error) bool { } // NewEstimator returns the estimator for a given config -func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei] { +func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] { s := cfg.GasEstimatorMode() lggr.Infow(fmt.Sprintf("Initializing EVM gas estimator in mode: %s", s), "estimatorMode", s, @@ -70,15 +70,49 @@ type DynamicFee struct { TipCap *assets.Wei } -type PriorAttempt interface { - GetGasPrice() *assets.Wei - DynamicFee() DynamicFee +type PriorAttempt[FEE any, HASH any] interface { + Fee() FEE GetChainSpecificGasLimit() uint32 GetBroadcastBeforeBlockNum() *int64 - GetHash() common.Hash + GetHash() HASH GetTxType() int } +type EvmPriorAttempt interface { + PriorAttempt[EvmFee, common.Hash] + + GetGasPrice() *assets.Wei + DynamicFee() DynamicFee +} + +type evmPriorAttempt struct { + PriorAttempt[EvmFee, common.Hash] +} + +func (e evmPriorAttempt) GetGasPrice() *assets.Wei { + return e.Fee().Legacy +} + +func (e evmPriorAttempt) DynamicFee() DynamicFee { + fee := e.Fee().Dynamic + if fee == nil { + return DynamicFee{} + } + return *fee +} + +func MakeEvmPriorAttempts(attempts []PriorAttempt[EvmFee, common.Hash]) (out []EvmPriorAttempt) { + for i := range attempts { + out = append(out, MakeEvmPriorAttempt(attempts[i])) + } + return out +} + +func MakeEvmPriorAttempt(a PriorAttempt[EvmFee, common.Hash]) EvmPriorAttempt { + e := evmPriorAttempt{a} + return &e +} + // Estimator provides an interface for estimating gas price and limit // //go:generate mockery --quiet --name EvmEstimator --output ./mocks/ --case=underscore @@ -94,7 +128,7 @@ type EvmEstimator interface { // attempts must: // - be sorted in order from highest price to lowest price // - all be of transaction type 0x0 or 0x1 - BumpLegacyGas(ctx context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) + BumpLegacyGas(ctx context.Context, originalGasPrice *assets.Wei, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []EvmPriorAttempt) (bumpedGasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) // GetDynamicFee Calculates initial gas fee for gas for EIP1559 transactions // maxGasPriceWei parameter is the highest possible gas fee cap that the function will return GetDynamicFee(ctx context.Context, gasLimit uint32, maxGasPriceWei *assets.Wei) (fee DynamicFee, chainSpecificGasLimit uint32, err error) @@ -103,7 +137,7 @@ type EvmEstimator interface { // attempts must: // - be sorted in order from highest price to lowest price // - all be of transaction type 0x2 - BumpDynamicFee(ctx context.Context, original DynamicFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []PriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) + BumpDynamicFee(ctx context.Context, original DynamicFee, gasLimit uint32, maxGasPriceWei *assets.Wei, attempts []EvmPriorAttempt) (bumped DynamicFee, chainSpecificGasLimit uint32, err error) } type EvmFee struct { @@ -114,13 +148,13 @@ type EvmFee struct { // FeeEstimator provides a generic interface for fee estimation // //go:generate mockery --quiet --name FeeEstimator --output ./mocks/ --case=underscore -type FeeEstimator[HEAD any, FEE any, MAXPRICE any] interface { +type FeeEstimator[HEAD any, FEE any, MAXPRICE any, HASH any] interface { OnNewLongestChain(context.Context, HEAD) Start(context.Context) error Close() error GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...Opt) (fee FEE, chainSpecificFeeLimit uint32, err error) - BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []PriorAttempt) (bumpedFee FEE, chainSpecificFeeLimit uint32, err error) + BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []PriorAttempt[FEE, HASH]) (bumpedFee FEE, chainSpecificFeeLimit uint32, err error) } // WrappedEvmEstimator provides a struct that wraps the EVM specific dynamic and legacy estimators into one estimator that conforms to the generic FeeEstimator @@ -130,9 +164,9 @@ type WrappedEvmEstimator struct { } // var _ FeeEstimator = (*WrappedEvmEstimator)(nil) -var _ FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei] = (*WrappedEvmEstimator)(nil) +var _ FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] = (*WrappedEvmEstimator)(nil) -func NewWrappedEvmEstimator(e EvmEstimator, cfg Config) FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei] { +func NewWrappedEvmEstimator(e EvmEstimator, cfg Config) FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] { return &WrappedEvmEstimator{ EvmEstimator: e, EIP1559Enabled: cfg.EvmEIP1559DynamicFees(), @@ -153,24 +187,27 @@ func (e WrappedEvmEstimator) GetFee(ctx context.Context, calldata []byte, feeLim return } -func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, feeLimit uint32, maxFeePrice *assets.Wei, attempts []PriorAttempt) (bumpedFee EvmFee, chainSpecificFeeLimit uint32, err error) { +func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, feeLimit uint32, maxFeePrice *assets.Wei, attempts []PriorAttempt[EvmFee, common.Hash]) (bumpedFee EvmFee, chainSpecificFeeLimit uint32, err error) { // validate only 1 fee type is present if (originalFee.Dynamic == nil && originalFee.Legacy == nil) || (originalFee.Dynamic != nil && originalFee.Legacy != nil) { err = errors.New("only one dynamic or legacy fee can be defined") return } + // convert PriorAttempts to EvmPriorAttempts + evmAttempts := MakeEvmPriorAttempts(attempts) + // bump fee based on what fee the tx has previously used (not based on config) // bump dynamic original if originalFee.Dynamic != nil { var bumpedDynamic DynamicFee - bumpedDynamic, chainSpecificFeeLimit, err = e.EvmEstimator.BumpDynamicFee(ctx, *originalFee.Dynamic, feeLimit, maxFeePrice, attempts) + bumpedDynamic, chainSpecificFeeLimit, err = e.EvmEstimator.BumpDynamicFee(ctx, *originalFee.Dynamic, feeLimit, maxFeePrice, evmAttempts) bumpedFee.Dynamic = &bumpedDynamic return } // bump legacy fee - bumpedFee.Legacy, chainSpecificFeeLimit, err = e.EvmEstimator.BumpLegacyGas(ctx, originalFee.Legacy, feeLimit, maxFeePrice, attempts) + bumpedFee.Legacy, chainSpecificFeeLimit, err = e.EvmEstimator.BumpLegacyGas(ctx, originalFee.Legacy, feeLimit, maxFeePrice, evmAttempts) return } diff --git a/core/chains/evm/txmgr/eth_broadcaster.go b/core/chains/evm/txmgr/eth_broadcaster.go index 256be8374bb..3c2c1e082d8 100644 --- a/core/chains/evm/txmgr/eth_broadcaster.go +++ b/core/chains/evm/txmgr/eth_broadcaster.go @@ -9,6 +9,7 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" "github.com/jackc/pgconn" "github.com/jpillora/backoff" @@ -96,7 +97,7 @@ type EthBroadcaster struct { q pg.Q ethClient evmclient.Client ChainKeyStore - estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] + estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] resumeCallback ResumeCallback // autoSyncNonce, if set, will cause EthBroadcaster to fast-forward the nonce @@ -124,7 +125,7 @@ type EthBroadcaster struct { // NewEthBroadcaster returns a new concrete EthBroadcaster func NewEthBroadcaster(db *sqlx.DB, ethClient evmclient.Client, config Config, keystore KeyStore, eventBroadcaster pg.EventBroadcaster, - keyStates []ethkey.State, estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], resumeCallback ResumeCallback, + keyStates []ethkey.State, estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], resumeCallback ResumeCallback, logger logger.Logger, checkerFactory TransmitCheckerFactory, autoSyncNonce bool) *EthBroadcaster { triggers := make(map[gethCommon.Address]chan struct{}) @@ -826,7 +827,7 @@ func (eb *EthBroadcaster) tryAgainWithNewDynamicFeeGas(ctx context.Context, lgr if err = eb.orm.SaveReplacementInProgressAttempt(attempt, &replacementAttempt); err != nil { return errors.Wrap(err, "tryAgainWithNewDynamicFeeGas failed"), true } - lgr.Debugw("Bumped dynamic fee gas on initial send", "oldFee", attempt.DynamicFee(), "newFee", newDynamicFee) + lgr.Debugw("Bumped dynamic fee gas on initial send", "oldFee", gas.MakeEvmPriorAttempt(attempt).DynamicFee(), "newFee", newDynamicFee) return eb.handleInProgressEthTx(ctx, etx, replacementAttempt, initialBroadcastAt) } diff --git a/core/chains/evm/txmgr/eth_broadcaster_test.go b/core/chains/evm/txmgr/eth_broadcaster_test.go index 240f54198a3..6d4944f6645 100644 --- a/core/chains/evm/txmgr/eth_broadcaster_test.go +++ b/core/chains/evm/txmgr/eth_broadcaster_test.go @@ -11,6 +11,7 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" gethTypes "github.com/ethereum/go-ethereum/core/types" @@ -577,7 +578,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi chStartEstimate := make(chan struct{}) chBlock := make(chan struct{}) - estimator := gasmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei](t) + estimator := gasmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash](t) estimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, evmcfg.KeySpecificMaxGasPriceWei(fromAddress)).Return(gas.EvmFee{Legacy: assets.GWei(32)}, uint32(500), nil).Run(func(_ mock.Arguments) { close(chStartEstimate) <-chBlock diff --git a/core/chains/evm/txmgr/eth_confirmer.go b/core/chains/evm/txmgr/eth_confirmer.go index 48dee5cb537..802374c0226 100644 --- a/core/chains/evm/txmgr/eth_confirmer.go +++ b/core/chains/evm/txmgr/eth_confirmer.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" @@ -117,7 +118,7 @@ type EthConfirmer struct { lggr logger.Logger ethClient evmclient.Client ChainKeyStore - estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] + estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] resumeCallback ResumeCallback keyStates []ethkey.State @@ -132,7 +133,7 @@ type EthConfirmer struct { // NewEthConfirmer instantiates a new eth confirmer func NewEthConfirmer(orm ORM, ethClient evmclient.Client, config Config, keystore KeyStore, - keyStates []ethkey.State, estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { + keyStates []ethkey.State, estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { ctx, cancel := context.WithCancel(context.Background()) lggr = lggr.Named("EthConfirmer") @@ -758,7 +759,7 @@ func (ec *EthConfirmer) logFieldsPreviousAttempt(attempt EthTxAttempt) []interfa } func (ec *EthConfirmer) bumpGas(ctx context.Context, etx EthTx, previousAttempts []EthTxAttempt) (bumpedAttempt EthTxAttempt, err error) { - priorAttempts := make([]gas.PriorAttempt, len(previousAttempts)) + priorAttempts := make([]gas.PriorAttempt[gas.EvmFee, common.Hash], len(previousAttempts)) // This feels a bit useless but until we get iterators there is no other // way to cast an array of structs to an array of interfaces for i, attempt := range previousAttempts { diff --git a/core/chains/evm/txmgr/mocks/tx_manager.go b/core/chains/evm/txmgr/mocks/tx_manager.go index b32417e684a..8bd5b394481 100644 --- a/core/chains/evm/txmgr/mocks/tx_manager.go +++ b/core/chains/evm/txmgr/mocks/tx_manager.go @@ -99,15 +99,15 @@ func (_m *TxManager) GetForwarderForEOA(eoa common.Address) (common.Address, err } // GetGasEstimator provides a mock function with given fields: -func (_m *TxManager) GetGasEstimator() gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei] { +func (_m *TxManager) GetGasEstimator() gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei, common.Hash] { ret := _m.Called() - var r0 gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei] - if rf, ok := ret.Get(0).(func() gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei]); ok { + var r0 gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei, common.Hash] + if rf, ok := ret.Get(0).(func() gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei, common.Hash]); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei]) + r0 = ret.Get(0).(gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei, common.Hash]) } } diff --git a/core/chains/evm/txmgr/models.go b/core/chains/evm/txmgr/models.go index 0ab48d828e3..4a2573188ad 100644 --- a/core/chains/evm/txmgr/models.go +++ b/core/chains/evm/txmgr/models.go @@ -279,7 +279,7 @@ func (e EthTx) GetChecker() (TransmitCheckerSpec, error) { return t, errors.Wrap(json.Unmarshal(*e.TransmitChecker, &t), "unmarshalling transmit checker") } -var _ gas.PriorAttempt = EthTxAttempt{} +var _ gas.PriorAttempt[gas.EvmFee, common.Hash] = EthTxAttempt{} type EthTxAttempt struct { ID int64 @@ -312,9 +312,9 @@ func (a EthTxAttempt) GetSignedTx() (*types.Transaction, error) { } func (a EthTxAttempt) Fee() (fee gas.EvmFee) { - fee.Legacy = a.GetGasPrice() + fee.Legacy = a.getGasPrice() - dynamic := a.DynamicFee() + dynamic := a.dynamicFee() // add dynamic struct only if values are not nil if dynamic.FeeCap != nil && dynamic.TipCap != nil { fee.Dynamic = &dynamic @@ -322,7 +322,7 @@ func (a EthTxAttempt) Fee() (fee gas.EvmFee) { return fee } -func (a EthTxAttempt) DynamicFee() gas.DynamicFee { +func (a EthTxAttempt) dynamicFee() gas.DynamicFee { return gas.DynamicFee{ FeeCap: a.GasFeeCap, TipCap: a.GasTipCap, @@ -337,7 +337,7 @@ func (a EthTxAttempt) GetChainSpecificGasLimit() uint32 { return a.ChainSpecificGasLimit } -func (a EthTxAttempt) GetGasPrice() *assets.Wei { +func (a EthTxAttempt) getGasPrice() *assets.Wei { return a.GasPrice } diff --git a/core/chains/evm/txmgr/txmgr.go b/core/chains/evm/txmgr/txmgr.go index 530dfe67021..3ed9c0a0b35 100644 --- a/core/chains/evm/txmgr/txmgr.go +++ b/core/chains/evm/txmgr/txmgr.go @@ -79,7 +79,7 @@ type TxManager interface { Trigger(addr common.Address) CreateEthTransaction(newTx NewTx, qopts ...pg.QOpt) (etx EthTx, err error) GetForwarderForEOA(eoa common.Address) (forwarder common.Address, err error) - GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] + GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] RegisterResumeCallback(fn ResumeCallback) SendEther(chainID *big.Int, from, to common.Address, value assets.Eth, gasLimit uint32) (etx EthTx, err error) Reset(f func(), addr common.Address, abandon bool) error @@ -104,7 +104,7 @@ type Txm struct { config Config keyStore KeyStore eventBroadcaster pg.EventBroadcaster - gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] + gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] chainID big.Int checkerFactory TransmitCheckerFactory @@ -556,7 +556,7 @@ func (b *Txm) checkEnabled(addr common.Address) error { } // GetGasEstimator returns the gas estimator, mostly useful for tests -func (b *Txm) GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] { +func (b *Txm) GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] { return b.gasEstimator } @@ -728,7 +728,7 @@ func (n *NullTxManager) Healthy() error { return nil } func (n *NullTxManager) Ready() error { return nil } func (n *NullTxManager) Name() string { return "" } func (n *NullTxManager) HealthReport() map[string]error { return nil } -func (n *NullTxManager) GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] { +func (n *NullTxManager) GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] { return nil } func (n *NullTxManager) RegisterResumeCallback(fn ResumeCallback) {} diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index 9135d4e15a4..bbda9b25453 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -51,7 +51,7 @@ type UpkeepExecuter struct { config Config executionQueue chan struct{} headBroadcaster httypes.HeadBroadcasterRegistry - gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] + gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] job job.Job mailbox *utils.Mailbox[*evmtypes.Head] orm ORM @@ -69,7 +69,7 @@ func NewUpkeepExecuter( pr pipeline.Runner, ethClient evmclient.Client, headBroadcaster httypes.HeadBroadcaster, - gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], + gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], logger logger.Logger, config Config, effectiveKeeperAddress common.Address, diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index a590f78dbba..b7a0bdde7ba 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -40,10 +40,10 @@ func newHead() evmtypes.Head { return evmtypes.NewHead(big.NewInt(20), utils.NewHash(), utils.NewHash(), 1000, utils.NewBigI(0)) } -func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei]) { +func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash]) { // note: estimator will only return 1 of legacy or dynamic fees (not both) // assumed to call legacy estimator only - estimator = gasmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei](t) + estimator = gasmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash](t) estimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe().Return(gas.EvmFee{ Legacy: assets.GWei(60), Dynamic: nil, @@ -51,7 +51,7 @@ func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator[*evmtypes.Hea return } -func setup(t *testing.T, estimator *gasmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) ( +func setup(t *testing.T, estimator *gasmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) ( *sqlx.DB, config.GeneralConfig, *evmmocks.Client, diff --git a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go index 6a9818fdc66..5b79636b718 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go +++ b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go @@ -4,6 +4,7 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ocr2vrf/types" "github.com/smartcontractkit/chainlink/core/assets" @@ -13,7 +14,7 @@ import ( // reasonableGasPriceProvider provides an estimate for the average gas price type reasonableGasPriceProvider struct { - estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei] + estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] timeout time.Duration maxGasPrice *assets.Wei supportsDynamicFee bool @@ -22,7 +23,7 @@ type reasonableGasPriceProvider struct { var _ types.ReasonableGasPrice = (*reasonableGasPriceProvider)(nil) func NewReasonableGasPriceProvider( - estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei], + estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], timeout time.Duration, maxGasPrice *assets.Wei, supportsDynamicFee bool, From 94c99d8897d9fb5c4c98aa8e0d54fd5463f39db3 Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Tue, 7 Mar 2023 09:09:55 -0700 Subject: [PATCH 09/10] move generic fee estimator types to common/txmgr/types + small feedback fixes --- common/txmgr/types/fee_estimator.go | 32 ++++++++++++ .../txmgr/types}/mocks/fee_estimator.go | 22 ++++----- core/chainlink.Dockerfile | 1 + core/chains/evm/gas/arbitrum_estimator.go | 5 +- .../chains/evm/gas/block_history_estimator.go | 3 +- .../evm/gas/block_history_estimator_test.go | 23 ++++----- core/chains/evm/gas/cmd/arbgas/main.go | 5 +- core/chains/evm/gas/fixed_price_estimator.go | 3 +- core/chains/evm/gas/helpers_test.go | 3 +- core/chains/evm/gas/l2_suggested_estimator.go | 5 +- core/chains/evm/gas/mocks/evm_estimator.go | 16 +++--- core/chains/evm/gas/models.go | 49 +++++-------------- core/chains/evm/gas/models_test.go | 1 + core/chains/evm/txmgr/eth_broadcaster.go | 8 +-- core/chains/evm/txmgr/eth_broadcaster_test.go | 5 +- core/chains/evm/txmgr/eth_confirmer.go | 8 +-- core/chains/evm/txmgr/mocks/tx_manager.go | 14 +++--- core/chains/evm/txmgr/models.go | 3 +- core/chains/evm/txmgr/txmgr.go | 9 ++-- core/services/keeper/upkeep_executer.go | 5 +- core/services/keeper/upkeep_executer_test.go | 8 +-- .../reasonable_gas_price_provider.go | 5 +- 22 files changed, 128 insertions(+), 105 deletions(-) create mode 100644 common/txmgr/types/fee_estimator.go rename {core/chains/evm/gas => common/txmgr/types}/mocks/fee_estimator.go (86%) diff --git a/common/txmgr/types/fee_estimator.go b/common/txmgr/types/fee_estimator.go new file mode 100644 index 00000000000..c9f2b66872e --- /dev/null +++ b/common/txmgr/types/fee_estimator.go @@ -0,0 +1,32 @@ +package types + +import "context" + +// Opt is an option for a gas estimator +type Opt int + +const ( + // OptForceRefetch forces the estimator to bust a cache if necessary + OptForceRefetch Opt = iota +) + +// PriorAttempt provides a generic interface for reading tx data to be used in the fee esimators +type PriorAttempt[FEE any, HASH any] interface { + Fee() FEE + GetChainSpecificGasLimit() uint32 + GetBroadcastBeforeBlockNum() *int64 + GetHash() HASH + GetTxType() int +} + +// FeeEstimator provides a generic interface for fee estimation +// +//go:generate mockery --quiet --name FeeEstimator --output ./mocks/ --case=underscore +type FeeEstimator[HEAD any, FEE any, MAXPRICE any, HASH any] interface { + OnNewLongestChain(context.Context, HEAD) + Start(context.Context) error + Close() error + + GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...Opt) (fee FEE, chainSpecificFeeLimit uint32, err error) + BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []PriorAttempt[FEE, HASH]) (bumpedFee FEE, chainSpecificFeeLimit uint32, err error) +} diff --git a/core/chains/evm/gas/mocks/fee_estimator.go b/common/txmgr/types/mocks/fee_estimator.go similarity index 86% rename from core/chains/evm/gas/mocks/fee_estimator.go rename to common/txmgr/types/mocks/fee_estimator.go index 0b6b7baa8d5..9b1c497d424 100644 --- a/core/chains/evm/gas/mocks/fee_estimator.go +++ b/common/txmgr/types/mocks/fee_estimator.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - gas "github.com/smartcontractkit/chainlink/core/chains/evm/gas" + types "github.com/smartcontractkit/chainlink/common/txmgr/types" mock "github.com/stretchr/testify/mock" ) @@ -15,28 +15,28 @@ type FeeEstimator[HEAD interface{}, FEE interface{}, MAXPRICE interface{}, HASH } // BumpFee provides a mock function with given fields: ctx, originalFee, feeLimit, maxFeePrice, attempts -func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []gas.PriorAttempt[FEE, HASH]) (FEE, uint32, error) { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []types.PriorAttempt[FEE, HASH]) (FEE, uint32, error) { ret := _m.Called(ctx, originalFee, feeLimit, maxFeePrice, attempts) var r0 FEE var r1 uint32 var r2 error - if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt[FEE, HASH]) (FEE, uint32, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []types.PriorAttempt[FEE, HASH]) (FEE, uint32, error)); ok { return rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } - if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt[FEE, HASH]) FEE); ok { + if rf, ok := ret.Get(0).(func(context.Context, FEE, uint32, MAXPRICE, []types.PriorAttempt[FEE, HASH]) FEE); ok { r0 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { r0 = ret.Get(0).(FEE) } - if rf, ok := ret.Get(1).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt[FEE, HASH]) uint32); ok { + if rf, ok := ret.Get(1).(func(context.Context, FEE, uint32, MAXPRICE, []types.PriorAttempt[FEE, HASH]) uint32); ok { r1 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { r1 = ret.Get(1).(uint32) } - if rf, ok := ret.Get(2).(func(context.Context, FEE, uint32, MAXPRICE, []gas.PriorAttempt[FEE, HASH]) error); ok { + if rf, ok := ret.Get(2).(func(context.Context, FEE, uint32, MAXPRICE, []types.PriorAttempt[FEE, HASH]) error); ok { r2 = rf(ctx, originalFee, feeLimit, maxFeePrice, attempts) } else { r2 = ret.Error(2) @@ -60,7 +60,7 @@ func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) Close() error { } // GetFee provides a mock function with given fields: ctx, calldata, feeLimit, maxFeePrice, opts -func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...gas.Opt) (FEE, uint32, error) { +func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...types.Opt) (FEE, uint32, error) { _va := make([]interface{}, len(opts)) for _i := range opts { _va[_i] = opts[_i] @@ -73,22 +73,22 @@ func (_m *FeeEstimator[HEAD, FEE, MAXPRICE, HASH]) GetFee(ctx context.Context, c var r0 FEE var r1 uint32 var r2 error - if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, MAXPRICE, ...gas.Opt) (FEE, uint32, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, MAXPRICE, ...types.Opt) (FEE, uint32, error)); ok { return rf(ctx, calldata, feeLimit, maxFeePrice, opts...) } - if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, MAXPRICE, ...gas.Opt) FEE); ok { + if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, MAXPRICE, ...types.Opt) FEE); ok { r0 = rf(ctx, calldata, feeLimit, maxFeePrice, opts...) } else { r0 = ret.Get(0).(FEE) } - if rf, ok := ret.Get(1).(func(context.Context, []byte, uint32, MAXPRICE, ...gas.Opt) uint32); ok { + if rf, ok := ret.Get(1).(func(context.Context, []byte, uint32, MAXPRICE, ...types.Opt) uint32); ok { r1 = rf(ctx, calldata, feeLimit, maxFeePrice, opts...) } else { r1 = ret.Get(1).(uint32) } - if rf, ok := ret.Get(2).(func(context.Context, []byte, uint32, MAXPRICE, ...gas.Opt) error); ok { + if rf, ok := ret.Get(2).(func(context.Context, []byte, uint32, MAXPRICE, ...types.Opt) error); ok { r2 = rf(ctx, calldata, feeLimit, maxFeePrice, opts...) } else { r2 = ret.Error(2) diff --git a/core/chainlink.Dockerfile b/core/chainlink.Dockerfile index b3fbe0b7ac1..369c8b28464 100644 --- a/core/chainlink.Dockerfile +++ b/core/chainlink.Dockerfile @@ -12,6 +12,7 @@ RUN go mod download # Env vars needed for chainlink build ARG COMMIT_SHA +COPY common common COPY core core COPY operator_ui operator_ui diff --git a/core/chains/evm/gas/arbitrum_estimator.go b/core/chains/evm/gas/arbitrum_estimator.go index dbd8ca8b19f..259ef28711b 100644 --- a/core/chains/evm/gas/arbitrum_estimator.go +++ b/core/chains/evm/gas/arbitrum_estimator.go @@ -13,6 +13,7 @@ import ( "github.com/pkg/errors" "golang.org/x/exp/slices" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/logger" @@ -89,13 +90,13 @@ func (a *arbitrumEstimator) Close() error { // - Limit is computed from the dynamic values perL2Tx and perL1CalldataUnit, provided by the getPricesInArbGas() method // of the precompilie contract at ArbGasInfoAddress. perL2Tx is a constant amount of gas, and perL1CalldataUnit is // multiplied by the length of the tx calldata. The sum of these two values plus the original l2GasLimit is returned. -func (a *arbitrumEstimator) GetLegacyGas(ctx context.Context, calldata []byte, l2GasLimit uint32, maxGasPriceWei *assets.Wei, opts ...Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { +func (a *arbitrumEstimator) GetLegacyGas(ctx context.Context, calldata []byte, l2GasLimit uint32, maxGasPriceWei *assets.Wei, opts ...txmgrtypes.Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { gasPrice, _, err = a.EvmEstimator.GetLegacyGas(ctx, calldata, l2GasLimit, maxGasPriceWei, opts...) if err != nil { return } ok := a.IfStarted(func() { - if slices.Contains(opts, OptForceRefetch) { + if slices.Contains(opts, txmgrtypes.OptForceRefetch) { ch := make(chan struct{}) select { case a.chForceRefetch <- ch: diff --git a/core/chains/evm/gas/block_history_estimator.go b/core/chains/evm/gas/block_history_estimator.go index f0688a1a027..250130deb66 100644 --- a/core/chains/evm/gas/block_history_estimator.go +++ b/core/chains/evm/gas/block_history_estimator.go @@ -15,6 +15,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" @@ -225,7 +226,7 @@ func (b *BlockHistoryEstimator) HealthReport() map[string]error { return map[string]error{b.Name(): b.Healthy()} } -func (b *BlockHistoryEstimator) GetLegacyGas(_ context.Context, _ []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, _ ...Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { +func (b *BlockHistoryEstimator) GetLegacyGas(_ context.Context, _ []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, _ ...txmgrtypes.Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { ok := b.IfStarted(func() { chainSpecificGasLimit = applyMultiplier(gasLimit, b.config.EvmGasLimitMultiplier()) gasPrice = b.getGasPrice() diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go index 6685e5b8cf8..7e9b7f24b75 100644 --- a/core/chains/evm/gas/block_history_estimator_test.go +++ b/core/chains/evm/gas/block_history_estimator_test.go @@ -18,6 +18,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" @@ -1806,7 +1807,7 @@ func TestBlockHistoryEstimator_GetDynamicFee(t *testing.T) { }) } -var _ gas.PriorAttempt[gas.EvmFee, common.Hash] = &MockAttempt{} +var _ txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash] = &MockAttempt{} type MockAttempt struct { BroadcastBeforeBlockNum *int64 @@ -1863,7 +1864,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { gas.NewBlockHistoryEstimator(lggr, nil, cfg, *testutils.NewRandomEVMChainID()), ) - attempts := []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts := []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x0, Hash: utils.NewHash()}, } @@ -1928,7 +1929,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { num := int64(0) hash := utils.NewHash() - attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts = []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x3, BroadcastBeforeBlockNum: &num, Hash: hash}, } @@ -1938,7 +1939,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { assert.Contains(t, err.Error(), fmt.Sprintf("attempt %s has unknown transaction type 0x3", hash)) }) - attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts = []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x0, BroadcastBeforeBlockNum: &num, Hash: hash}, } @@ -1975,7 +1976,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { } gas.SetRollingBlockHistory(bhe, []evmtypes.Block{b0, b1, b2, b3}) - attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts = []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(1000), BroadcastBeforeBlockNum: testutils.Ptr(int64(4))}, // This is very expensive but will be ignored due to BroadcastBeforeBlockNum being too recent &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(3), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(5), BroadcastBeforeBlockNum: testutils.Ptr(int64(1))}, @@ -2024,7 +2025,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { } gas.SetRollingBlockHistory(bhe, []evmtypes.Block{b0}) - attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts = []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(1), GasTipCap: assets.NewWeiI(3), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(10), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, } @@ -2045,7 +2046,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { assert.Contains(t, err.Error(), fmt.Sprintf("transaction %s has gas price of 10 wei, which is above percentile=60%% (percentile price: 7 wei) for blocks 3 thru 3 (checking 1 blocks)", attempts[1].GetHash())) }) - attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts = []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(11), GasTipCap: assets.NewWeiI(10), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(3), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, } @@ -2093,7 +2094,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { blocks := []evmtypes.Block{b0, b1, b2, b3} gas.SetRollingBlockHistory(bhe, blocks) - attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts = []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(30), GasTipCap: assets.NewWeiI(1000), BroadcastBeforeBlockNum: testutils.Ptr(int64(4))}, // This is very expensive but will be ignored due to BroadcastBeforeBlockNum being too recent &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(30), GasTipCap: assets.NewWeiI(3), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(30), GasTipCap: assets.NewWeiI(5), BroadcastBeforeBlockNum: testutils.Ptr(int64(1))}, @@ -2128,7 +2129,7 @@ func TestBlockHistoryEstimator_CheckConnectivity(t *testing.T) { cfg.BlockHistoryEstimatorCheckInclusionBlocksF = 3 cfg.BlockHistoryEstimatorCheckInclusionPercentileF = 5 - attempts = []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts = []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasFeeCap: assets.NewWeiI(4), GasTipCap: assets.NewWeiI(7), BroadcastBeforeBlockNum: testutils.Ptr(int64(1))}, } @@ -2161,7 +2162,7 @@ func TestBlockHistoryEstimator_Bumps(t *testing.T) { head := cltest.Head(1) bhe.OnNewLongestChain(testutils.Context(t), head) - attempts := []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts := []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x0, Hash: utils.NewHash(), GasPrice: assets.NewWeiI(1000), BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, } @@ -2268,7 +2269,7 @@ func TestBlockHistoryEstimator_Bumps(t *testing.T) { bhe.OnNewLongestChain(testutils.Context(t), head) originalFee := gas.DynamicFee{FeeCap: assets.NewWeiI(100), TipCap: assets.NewWeiI(25)} - attempts := []gas.PriorAttempt[gas.EvmFee, common.Hash]{ + attempts := []txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash]{ &MockAttempt{TxType: 0x2, Hash: utils.NewHash(), GasTipCap: originalFee.TipCap, GasFeeCap: originalFee.FeeCap, BroadcastBeforeBlockNum: testutils.Ptr(int64(0))}, } diff --git a/core/chains/evm/gas/cmd/arbgas/main.go b/core/chains/evm/gas/cmd/arbgas/main.go index 708d2b61fa7..a14752d082d 100644 --- a/core/chains/evm/gas/cmd/arbgas/main.go +++ b/core/chains/evm/gas/cmd/arbgas/main.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/rpc" "go.uber.org/zap/zapcore" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" "github.com/smartcontractkit/chainlink/core/logger" @@ -28,12 +29,12 @@ func main() { ctx := context.Background() withEstimator(ctx, logger.Sugared(lggr), url, func(e gas.EvmEstimator) { printGetLegacyGas(ctx, e, make([]byte, 10), 500_000, assets.GWei(1)) - printGetLegacyGas(ctx, e, make([]byte, 10), 500_000, assets.GWei(1), gas.OptForceRefetch) + printGetLegacyGas(ctx, e, make([]byte, 10), 500_000, assets.GWei(1), txmgrtypes.OptForceRefetch) printGetLegacyGas(ctx, e, make([]byte, 10), max, assets.GWei(1)) }) } -func printGetLegacyGas(ctx context.Context, e gas.EvmEstimator, calldata []byte, l2GasLimit uint32, maxGasPrice *assets.Wei, opts ...gas.Opt) { +func printGetLegacyGas(ctx context.Context, e gas.EvmEstimator, calldata []byte, l2GasLimit uint32, maxGasPrice *assets.Wei, opts ...txmgrtypes.Opt) { price, limit, err := e.GetLegacyGas(ctx, calldata, l2GasLimit, maxGasPrice, opts...) if err != nil { log.Println("failed to get legacy gas:", err) diff --git a/core/chains/evm/gas/fixed_price_estimator.go b/core/chains/evm/gas/fixed_price_estimator.go index 823d1b457c9..cae703446e9 100644 --- a/core/chains/evm/gas/fixed_price_estimator.go +++ b/core/chains/evm/gas/fixed_price_estimator.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" "github.com/smartcontractkit/chainlink/core/logger" @@ -35,7 +36,7 @@ func (f *fixedPriceEstimator) Start(context.Context) error { func (f *fixedPriceEstimator) Close() error { return nil } func (f *fixedPriceEstimator) OnNewLongestChain(_ context.Context, _ *evmtypes.Head) {} -func (f *fixedPriceEstimator) GetLegacyGas(_ context.Context, _ []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, _ ...Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { +func (f *fixedPriceEstimator) GetLegacyGas(_ context.Context, _ []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, _ ...txmgrtypes.Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { gasPrice = f.config.EvmGasPriceDefault() chainSpecificGasLimit = applyMultiplier(gasLimit, f.config.EvmGasLimitMultiplier()) gasPrice = capGasPrice(gasPrice, maxGasPriceWei, f.config) diff --git a/core/chains/evm/gas/helpers_test.go b/core/chains/evm/gas/helpers_test.go index b9acdcb4c08..0b89ef1b4e6 100644 --- a/core/chains/evm/gas/helpers_test.go +++ b/core/chains/evm/gas/helpers_test.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" "github.com/smartcontractkit/chainlink/core/config" @@ -17,7 +18,7 @@ func init() { MaxStartTime = 1 * time.Second } -func (b *BlockHistoryEstimator) CheckConnectivity(attempts []PriorAttempt[EvmFee, common.Hash]) error { +func (b *BlockHistoryEstimator) CheckConnectivity(attempts []txmgrtypes.PriorAttempt[EvmFee, common.Hash]) error { return b.checkConnectivity(MakeEvmPriorAttempts(attempts)) } diff --git a/core/chains/evm/gas/l2_suggested_estimator.go b/core/chains/evm/gas/l2_suggested_estimator.go index 47aed66fb0c..e40d5e22f10 100644 --- a/core/chains/evm/gas/l2_suggested_estimator.go +++ b/core/chains/evm/gas/l2_suggested_estimator.go @@ -9,6 +9,7 @@ import ( "github.com/pkg/errors" "golang.org/x/exp/slices" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" @@ -123,11 +124,11 @@ func (*l2SuggestedPriceEstimator) BumpDynamicFee(_ context.Context, _ DynamicFee return } -func (o *l2SuggestedPriceEstimator) GetLegacyGas(ctx context.Context, _ []byte, l2GasLimit uint32, maxGasPriceWei *assets.Wei, opts ...Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { +func (o *l2SuggestedPriceEstimator) GetLegacyGas(ctx context.Context, _ []byte, l2GasLimit uint32, maxGasPriceWei *assets.Wei, opts ...txmgrtypes.Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) { chainSpecificGasLimit = l2GasLimit ok := o.IfStarted(func() { - if slices.Contains(opts, OptForceRefetch) { + if slices.Contains(opts, txmgrtypes.OptForceRefetch) { ch := make(chan struct{}) select { case o.chForceRefetch <- ch: diff --git a/core/chains/evm/gas/mocks/evm_estimator.go b/core/chains/evm/gas/mocks/evm_estimator.go index de1cf0a9312..f5a269de144 100644 --- a/core/chains/evm/gas/mocks/evm_estimator.go +++ b/core/chains/evm/gas/mocks/evm_estimator.go @@ -7,11 +7,13 @@ import ( assets "github.com/smartcontractkit/chainlink/core/assets" + evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" + gas "github.com/smartcontractkit/chainlink/core/chains/evm/gas" mock "github.com/stretchr/testify/mock" - types "github.com/smartcontractkit/chainlink/core/chains/evm/types" + types "github.com/smartcontractkit/chainlink/common/txmgr/types" ) // EvmEstimator is an autogenerated mock type for the EvmEstimator type @@ -129,7 +131,7 @@ func (_m *EvmEstimator) GetDynamicFee(ctx context.Context, gasLimit uint32, maxG } // GetLegacyGas provides a mock function with given fields: ctx, calldata, gasLimit, maxGasPriceWei, opts -func (_m *EvmEstimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...gas.Opt) (*assets.Wei, uint32, error) { +func (_m *EvmEstimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...types.Opt) (*assets.Wei, uint32, error) { _va := make([]interface{}, len(opts)) for _i := range opts { _va[_i] = opts[_i] @@ -142,10 +144,10 @@ func (_m *EvmEstimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLi var r0 *assets.Wei var r1 uint32 var r2 error - if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) (*assets.Wei, uint32, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, *assets.Wei, ...types.Opt) (*assets.Wei, uint32, error)); ok { return rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) } - if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) *assets.Wei); ok { + if rf, ok := ret.Get(0).(func(context.Context, []byte, uint32, *assets.Wei, ...types.Opt) *assets.Wei); ok { r0 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) } else { if ret.Get(0) != nil { @@ -153,13 +155,13 @@ func (_m *EvmEstimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLi } } - if rf, ok := ret.Get(1).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) uint32); ok { + if rf, ok := ret.Get(1).(func(context.Context, []byte, uint32, *assets.Wei, ...types.Opt) uint32); ok { r1 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) } else { r1 = ret.Get(1).(uint32) } - if rf, ok := ret.Get(2).(func(context.Context, []byte, uint32, *assets.Wei, ...gas.Opt) error); ok { + if rf, ok := ret.Get(2).(func(context.Context, []byte, uint32, *assets.Wei, ...types.Opt) error); ok { r2 = rf(ctx, calldata, gasLimit, maxGasPriceWei, opts...) } else { r2 = ret.Error(2) @@ -169,7 +171,7 @@ func (_m *EvmEstimator) GetLegacyGas(ctx context.Context, calldata []byte, gasLi } // OnNewLongestChain provides a mock function with given fields: _a0, _a1 -func (_m *EvmEstimator) OnNewLongestChain(_a0 context.Context, _a1 *types.Head) { +func (_m *EvmEstimator) OnNewLongestChain(_a0 context.Context, _a1 *evmtypes.Head) { _m.Called(_a0, _a1) } diff --git a/core/chains/evm/gas/models.go b/core/chains/evm/gas/models.go index f3a13480431..0f45b46bd82 100644 --- a/core/chains/evm/gas/models.go +++ b/core/chains/evm/gas/models.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/shopspring/decimal" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/chains/evm/label" @@ -28,7 +29,7 @@ func IsBumpErr(err error) bool { } // NewEstimator returns the estimator for a given config -func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] { +func NewEstimator(lggr logger.Logger, ethClient evmclient.Client, cfg Config) txmgrtypes.FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] { s := cfg.GasEstimatorMode() lggr.Infow(fmt.Sprintf("Initializing EVM gas estimator in mode: %s", s), "estimatorMode", s, @@ -70,23 +71,15 @@ type DynamicFee struct { TipCap *assets.Wei } -type PriorAttempt[FEE any, HASH any] interface { - Fee() FEE - GetChainSpecificGasLimit() uint32 - GetBroadcastBeforeBlockNum() *int64 - GetHash() HASH - GetTxType() int -} - type EvmPriorAttempt interface { - PriorAttempt[EvmFee, common.Hash] + txmgrtypes.PriorAttempt[EvmFee, common.Hash] GetGasPrice() *assets.Wei DynamicFee() DynamicFee } type evmPriorAttempt struct { - PriorAttempt[EvmFee, common.Hash] + txmgrtypes.PriorAttempt[EvmFee, common.Hash] } func (e evmPriorAttempt) GetGasPrice() *assets.Wei { @@ -101,14 +94,14 @@ func (e evmPriorAttempt) DynamicFee() DynamicFee { return *fee } -func MakeEvmPriorAttempts(attempts []PriorAttempt[EvmFee, common.Hash]) (out []EvmPriorAttempt) { +func MakeEvmPriorAttempts(attempts []txmgrtypes.PriorAttempt[EvmFee, common.Hash]) (out []EvmPriorAttempt) { for i := range attempts { out = append(out, MakeEvmPriorAttempt(attempts[i])) } return out } -func MakeEvmPriorAttempt(a PriorAttempt[EvmFee, common.Hash]) EvmPriorAttempt { +func MakeEvmPriorAttempt(a txmgrtypes.PriorAttempt[EvmFee, common.Hash]) EvmPriorAttempt { e := evmPriorAttempt{a} return &e } @@ -122,7 +115,7 @@ type EvmEstimator interface { Close() error // GetLegacyGas Calculates initial gas fee for non-EIP1559 transaction // maxGasPriceWei parameter is the highest possible gas fee cap that the function will return - GetLegacyGas(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) + GetLegacyGas(ctx context.Context, calldata []byte, gasLimit uint32, maxGasPriceWei *assets.Wei, opts ...txmgrtypes.Opt) (gasPrice *assets.Wei, chainSpecificGasLimit uint32, err error) // BumpLegacyGas Increases gas price and/or limit for non-EIP1559 transactions // if the bumped gas fee is greater than maxGasPriceWei, the method returns an error // attempts must: @@ -145,18 +138,6 @@ type EvmFee struct { Dynamic *DynamicFee } -// FeeEstimator provides a generic interface for fee estimation -// -//go:generate mockery --quiet --name FeeEstimator --output ./mocks/ --case=underscore -type FeeEstimator[HEAD any, FEE any, MAXPRICE any, HASH any] interface { - OnNewLongestChain(context.Context, HEAD) - Start(context.Context) error - Close() error - - GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice MAXPRICE, opts ...Opt) (fee FEE, chainSpecificFeeLimit uint32, err error) - BumpFee(ctx context.Context, originalFee FEE, feeLimit uint32, maxFeePrice MAXPRICE, attempts []PriorAttempt[FEE, HASH]) (bumpedFee FEE, chainSpecificFeeLimit uint32, err error) -} - // WrappedEvmEstimator provides a struct that wraps the EVM specific dynamic and legacy estimators into one estimator that conforms to the generic FeeEstimator type WrappedEvmEstimator struct { EvmEstimator @@ -164,16 +145,16 @@ type WrappedEvmEstimator struct { } // var _ FeeEstimator = (*WrappedEvmEstimator)(nil) -var _ FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] = (*WrappedEvmEstimator)(nil) +var _ txmgrtypes.FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] = (*WrappedEvmEstimator)(nil) -func NewWrappedEvmEstimator(e EvmEstimator, cfg Config) FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] { +func NewWrappedEvmEstimator(e EvmEstimator, cfg Config) txmgrtypes.FeeEstimator[*evmtypes.Head, EvmFee, *assets.Wei, common.Hash] { return &WrappedEvmEstimator{ EvmEstimator: e, EIP1559Enabled: cfg.EvmEIP1559DynamicFees(), } } -func (e WrappedEvmEstimator) GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice *assets.Wei, opts ...Opt) (fee EvmFee, chainSpecificFeeLimit uint32, err error) { +func (e WrappedEvmEstimator) GetFee(ctx context.Context, calldata []byte, feeLimit uint32, maxFeePrice *assets.Wei, opts ...txmgrtypes.Opt) (fee EvmFee, chainSpecificFeeLimit uint32, err error) { // get dynamic fee if e.EIP1559Enabled { var dynamicFee DynamicFee @@ -187,7 +168,7 @@ func (e WrappedEvmEstimator) GetFee(ctx context.Context, calldata []byte, feeLim return } -func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, feeLimit uint32, maxFeePrice *assets.Wei, attempts []PriorAttempt[EvmFee, common.Hash]) (bumpedFee EvmFee, chainSpecificFeeLimit uint32, err error) { +func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, feeLimit uint32, maxFeePrice *assets.Wei, attempts []txmgrtypes.PriorAttempt[EvmFee, common.Hash]) (bumpedFee EvmFee, chainSpecificFeeLimit uint32, err error) { // validate only 1 fee type is present if (originalFee.Dynamic == nil && originalFee.Legacy == nil) || (originalFee.Dynamic != nil && originalFee.Legacy != nil) { err = errors.New("only one dynamic or legacy fee can be defined") @@ -211,14 +192,6 @@ func (e WrappedEvmEstimator) BumpFee(ctx context.Context, originalFee EvmFee, fe return } -// Opt is an option for a gas estimator -type Opt int - -const ( - // OptForceRefetch forces the estimator to bust a cache if necessary - OptForceRefetch Opt = iota -) - func applyMultiplier(gasLimit uint32, multiplier float32) uint32 { return uint32(decimal.NewFromBigInt(big.NewInt(0).SetUint64(uint64(gasLimit)), 0).Mul(decimal.NewFromFloat32(multiplier)).IntPart()) } diff --git a/core/chains/evm/gas/models_test.go b/core/chains/evm/gas/models_test.go index 50bc085197a..61fc5c3d211 100644 --- a/core/chains/evm/gas/models_test.go +++ b/core/chains/evm/gas/models_test.go @@ -14,6 +14,7 @@ import ( ) func TestWrappedEvmEstimator(t *testing.T) { + t.Parallel() ctx := context.Background() // fee values diff --git a/core/chains/evm/txmgr/eth_broadcaster.go b/core/chains/evm/txmgr/eth_broadcaster.go index 3c2c1e082d8..f6007047e77 100644 --- a/core/chains/evm/txmgr/eth_broadcaster.go +++ b/core/chains/evm/txmgr/eth_broadcaster.go @@ -9,7 +9,6 @@ import ( "sync" "time" - "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" "github.com/jackc/pgconn" "github.com/jpillora/backoff" @@ -21,6 +20,7 @@ import ( "github.com/smartcontractkit/sqlx" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" @@ -97,7 +97,7 @@ type EthBroadcaster struct { q pg.Q ethClient evmclient.Client ChainKeyStore - estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] + estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, gethCommon.Hash] resumeCallback ResumeCallback // autoSyncNonce, if set, will cause EthBroadcaster to fast-forward the nonce @@ -125,7 +125,7 @@ type EthBroadcaster struct { // NewEthBroadcaster returns a new concrete EthBroadcaster func NewEthBroadcaster(db *sqlx.DB, ethClient evmclient.Client, config Config, keystore KeyStore, eventBroadcaster pg.EventBroadcaster, - keyStates []ethkey.State, estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], resumeCallback ResumeCallback, + keyStates []ethkey.State, estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, gethCommon.Hash], resumeCallback ResumeCallback, logger logger.Logger, checkerFactory TransmitCheckerFactory, autoSyncNonce bool) *EthBroadcaster { triggers := make(map[gethCommon.Address]chan struct{}) @@ -794,7 +794,7 @@ func (eb *EthBroadcaster) tryAgainWithNewEstimation(ctx context.Context, lgr log return err, false } keySpecificMaxGasPriceWei := eb.config.KeySpecificMaxGasPriceWei(etx.FromAddress) - gasPrice, gasLimit, err := eb.estimator.GetFee(ctx, etx.EncodedPayload, etx.GasLimit, keySpecificMaxGasPriceWei, gas.OptForceRefetch) + gasPrice, gasLimit, err := eb.estimator.GetFee(ctx, etx.EncodedPayload, etx.GasLimit, keySpecificMaxGasPriceWei, txmgrtypes.OptForceRefetch) if err != nil { return errors.Wrap(err, "tryAgainWithNewEstimation failed to estimate gas"), true } diff --git a/core/chains/evm/txmgr/eth_broadcaster_test.go b/core/chains/evm/txmgr/eth_broadcaster_test.go index 6d4944f6645..351e7399a91 100644 --- a/core/chains/evm/txmgr/eth_broadcaster_test.go +++ b/core/chains/evm/txmgr/eth_broadcaster_test.go @@ -11,7 +11,6 @@ import ( "testing" "time" - "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" gethTypes "github.com/ethereum/go-ethereum/core/types" @@ -24,10 +23,10 @@ import ( "go.uber.org/zap/zapcore" "gopkg.in/guregu/null.v4" + txmgrmocks "github.com/smartcontractkit/chainlink/common/txmgr/types/mocks" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" - gasmocks "github.com/smartcontractkit/chainlink/core/chains/evm/gas/mocks" "github.com/smartcontractkit/chainlink/core/chains/evm/txmgr" evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" "github.com/smartcontractkit/chainlink/core/internal/cltest" @@ -578,7 +577,7 @@ func TestEthBroadcaster_ProcessUnstartedEthTxs_OptimisticLockingOnEthTx(t *testi chStartEstimate := make(chan struct{}) chBlock := make(chan struct{}) - estimator := gasmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash](t) + estimator := txmgrmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, gethCommon.Hash](t) estimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, evmcfg.KeySpecificMaxGasPriceWei(fromAddress)).Return(gas.EvmFee{Legacy: assets.GWei(32)}, uint32(500), nil).Run(func(_ mock.Arguments) { close(chStartEstimate) <-chBlock diff --git a/core/chains/evm/txmgr/eth_confirmer.go b/core/chains/evm/txmgr/eth_confirmer.go index 802374c0226..07488cb8c21 100644 --- a/core/chains/evm/txmgr/eth_confirmer.go +++ b/core/chains/evm/txmgr/eth_confirmer.go @@ -19,6 +19,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promauto" "go.uber.org/multierr" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" @@ -118,7 +119,7 @@ type EthConfirmer struct { lggr logger.Logger ethClient evmclient.Client ChainKeyStore - estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] + estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] resumeCallback ResumeCallback keyStates []ethkey.State @@ -133,7 +134,7 @@ type EthConfirmer struct { // NewEthConfirmer instantiates a new eth confirmer func NewEthConfirmer(orm ORM, ethClient evmclient.Client, config Config, keystore KeyStore, - keyStates []ethkey.State, estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { + keyStates []ethkey.State, estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { ctx, cancel := context.WithCancel(context.Background()) lggr = lggr.Named("EthConfirmer") @@ -759,7 +760,8 @@ func (ec *EthConfirmer) logFieldsPreviousAttempt(attempt EthTxAttempt) []interfa } func (ec *EthConfirmer) bumpGas(ctx context.Context, etx EthTx, previousAttempts []EthTxAttempt) (bumpedAttempt EthTxAttempt, err error) { - priorAttempts := make([]gas.PriorAttempt[gas.EvmFee, common.Hash], len(previousAttempts)) + // TODO: once generics are introduced at the top level struct (EthConfirmer) remove the chain-specific typings + priorAttempts := make([]txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash], len(previousAttempts)) // This feels a bit useless but until we get iterators there is no other // way to cast an array of structs to an array of interfaces for i, attempt := range previousAttempts { diff --git a/core/chains/evm/txmgr/mocks/tx_manager.go b/core/chains/evm/txmgr/mocks/tx_manager.go index 8bd5b394481..435b6df63c5 100644 --- a/core/chains/evm/txmgr/mocks/tx_manager.go +++ b/core/chains/evm/txmgr/mocks/tx_manager.go @@ -11,6 +11,8 @@ import ( context "context" + evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" + gas "github.com/smartcontractkit/chainlink/core/chains/evm/gas" mock "github.com/stretchr/testify/mock" @@ -19,7 +21,7 @@ import ( txmgr "github.com/smartcontractkit/chainlink/core/chains/evm/txmgr" - types "github.com/smartcontractkit/chainlink/core/chains/evm/types" + types "github.com/smartcontractkit/chainlink/common/txmgr/types" ) // TxManager is an autogenerated mock type for the TxManager type @@ -99,15 +101,15 @@ func (_m *TxManager) GetForwarderForEOA(eoa common.Address) (common.Address, err } // GetGasEstimator provides a mock function with given fields: -func (_m *TxManager) GetGasEstimator() gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei, common.Hash] { +func (_m *TxManager) GetGasEstimator() types.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] { ret := _m.Called() - var r0 gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei, common.Hash] - if rf, ok := ret.Get(0).(func() gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei, common.Hash]); ok { + var r0 types.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] + if rf, ok := ret.Get(0).(func() types.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash]); ok { r0 = rf() } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(gas.FeeEstimator[*types.Head, gas.EvmFee, *assets.Wei, common.Hash]) + r0 = ret.Get(0).(types.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash]) } } @@ -159,7 +161,7 @@ func (_m *TxManager) Name() string { } // OnNewLongestChain provides a mock function with given fields: ctx, head -func (_m *TxManager) OnNewLongestChain(ctx context.Context, head *types.Head) { +func (_m *TxManager) OnNewLongestChain(ctx context.Context, head *evmtypes.Head) { _m.Called(ctx, head) } diff --git a/core/chains/evm/txmgr/models.go b/core/chains/evm/txmgr/models.go index 4a2573188ad..36ee8aba155 100644 --- a/core/chains/evm/txmgr/models.go +++ b/core/chains/evm/txmgr/models.go @@ -16,6 +16,7 @@ import ( uuid "github.com/satori/go.uuid" "gopkg.in/guregu/null.v4" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" @@ -279,7 +280,7 @@ func (e EthTx) GetChecker() (TransmitCheckerSpec, error) { return t, errors.Wrap(json.Unmarshal(*e.TransmitChecker, &t), "unmarshalling transmit checker") } -var _ gas.PriorAttempt[gas.EvmFee, common.Hash] = EthTxAttempt{} +var _ txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash] = EthTxAttempt{} type EthTxAttempt struct { ID int64 diff --git a/core/chains/evm/txmgr/txmgr.go b/core/chains/evm/txmgr/txmgr.go index 3ed9c0a0b35..6c2e6b0db0c 100644 --- a/core/chains/evm/txmgr/txmgr.go +++ b/core/chains/evm/txmgr/txmgr.go @@ -15,6 +15,7 @@ import ( uuid "github.com/satori/go.uuid" "github.com/smartcontractkit/sqlx" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/chains/evm/forwarders" @@ -79,7 +80,7 @@ type TxManager interface { Trigger(addr common.Address) CreateEthTransaction(newTx NewTx, qopts ...pg.QOpt) (etx EthTx, err error) GetForwarderForEOA(eoa common.Address) (forwarder common.Address, err error) - GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] + GetGasEstimator() txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] RegisterResumeCallback(fn ResumeCallback) SendEther(chainID *big.Int, from, to common.Address, value assets.Eth, gasLimit uint32) (etx EthTx, err error) Reset(f func(), addr common.Address, abandon bool) error @@ -104,7 +105,7 @@ type Txm struct { config Config keyStore KeyStore eventBroadcaster pg.EventBroadcaster - gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] + gasEstimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] chainID big.Int checkerFactory TransmitCheckerFactory @@ -556,7 +557,7 @@ func (b *Txm) checkEnabled(addr common.Address) error { } // GetGasEstimator returns the gas estimator, mostly useful for tests -func (b *Txm) GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] { +func (b *Txm) GetGasEstimator() txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] { return b.gasEstimator } @@ -728,7 +729,7 @@ func (n *NullTxManager) Healthy() error { return nil } func (n *NullTxManager) Ready() error { return nil } func (n *NullTxManager) Name() string { return "" } func (n *NullTxManager) HealthReport() map[string]error { return nil } -func (n *NullTxManager) GetGasEstimator() gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] { +func (n *NullTxManager) GetGasEstimator() txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] { return nil } func (n *NullTxManager) RegisterResumeCallback(fn ResumeCallback) {} diff --git a/core/services/keeper/upkeep_executer.go b/core/services/keeper/upkeep_executer.go index bbda9b25453..2dad25f5dbf 100644 --- a/core/services/keeper/upkeep_executer.go +++ b/core/services/keeper/upkeep_executer.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" evmclient "github.com/smartcontractkit/chainlink/core/chains/evm/client" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" @@ -51,7 +52,7 @@ type UpkeepExecuter struct { config Config executionQueue chan struct{} headBroadcaster httypes.HeadBroadcasterRegistry - gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] + gasEstimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] job job.Job mailbox *utils.Mailbox[*evmtypes.Head] orm ORM @@ -69,7 +70,7 @@ func NewUpkeepExecuter( pr pipeline.Runner, ethClient evmclient.Client, headBroadcaster httypes.HeadBroadcaster, - gasEstimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], + gasEstimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], logger logger.Logger, config Config, effectiveKeeperAddress common.Address, diff --git a/core/services/keeper/upkeep_executer_test.go b/core/services/keeper/upkeep_executer_test.go index b7a0bdde7ba..ec6b529280a 100644 --- a/core/services/keeper/upkeep_executer_test.go +++ b/core/services/keeper/upkeep_executer_test.go @@ -14,10 +14,10 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + txmgrmocks "github.com/smartcontractkit/chainlink/common/txmgr/types/mocks" "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" - gasmocks "github.com/smartcontractkit/chainlink/core/chains/evm/gas/mocks" evmmocks "github.com/smartcontractkit/chainlink/core/chains/evm/mocks" "github.com/smartcontractkit/chainlink/core/chains/evm/txmgr" txmmocks "github.com/smartcontractkit/chainlink/core/chains/evm/txmgr/mocks" @@ -40,10 +40,10 @@ func newHead() evmtypes.Head { return evmtypes.NewHead(big.NewInt(20), utils.NewHash(), utils.NewHash(), 1000, utils.NewBigI(0)) } -func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash]) { +func mockEstimator(t *testing.T) (estimator *txmgrmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash]) { // note: estimator will only return 1 of legacy or dynamic fees (not both) // assumed to call legacy estimator only - estimator = gasmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash](t) + estimator = txmgrmocks.NewFeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash](t) estimator.On("GetFee", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe().Return(gas.EvmFee{ Legacy: assets.GWei(60), Dynamic: nil, @@ -51,7 +51,7 @@ func mockEstimator(t *testing.T) (estimator *gasmocks.FeeEstimator[*evmtypes.Hea return } -func setup(t *testing.T, estimator *gasmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) ( +func setup(t *testing.T, estimator *txmgrmocks.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], overrideFn func(c *chainlink.Config, s *chainlink.Secrets)) ( *sqlx.DB, config.GeneralConfig, *evmmocks.Client, diff --git a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go index 5b79636b718..430a57dd790 100644 --- a/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go +++ b/core/services/ocr2/plugins/ocr2vrf/reasonablegasprice/reasonable_gas_price_provider.go @@ -7,6 +7,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/smartcontractkit/ocr2vrf/types" + txmgrtypes "github.com/smartcontractkit/chainlink/common/txmgr/types" "github.com/smartcontractkit/chainlink/core/assets" "github.com/smartcontractkit/chainlink/core/chains/evm/gas" evmtypes "github.com/smartcontractkit/chainlink/core/chains/evm/types" @@ -14,7 +15,7 @@ import ( // reasonableGasPriceProvider provides an estimate for the average gas price type reasonableGasPriceProvider struct { - estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] + estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] timeout time.Duration maxGasPrice *assets.Wei supportsDynamicFee bool @@ -23,7 +24,7 @@ type reasonableGasPriceProvider struct { var _ types.ReasonableGasPrice = (*reasonableGasPriceProvider)(nil) func NewReasonableGasPriceProvider( - estimator gas.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], + estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], timeout time.Duration, maxGasPrice *assets.Wei, supportsDynamicFee bool, From 21043ffc3d4327d39f172460e6972eed381164b0 Mon Sep 17 00:00:00 2001 From: aalu1418 <50029043+aalu1418@users.noreply.github.com> Date: Tue, 7 Mar 2023 11:00:02 -0700 Subject: [PATCH 10/10] remove duplicate geth/common import --- core/chains/evm/txmgr/eth_confirmer.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/chains/evm/txmgr/eth_confirmer.go b/core/chains/evm/txmgr/eth_confirmer.go index 07488cb8c21..93b66f7b8df 100644 --- a/core/chains/evm/txmgr/eth_confirmer.go +++ b/core/chains/evm/txmgr/eth_confirmer.go @@ -10,7 +10,6 @@ import ( "time" "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" gethCommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" @@ -119,7 +118,7 @@ type EthConfirmer struct { lggr logger.Logger ethClient evmclient.Client ChainKeyStore - estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash] + estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, gethCommon.Hash] resumeCallback ResumeCallback keyStates []ethkey.State @@ -134,7 +133,7 @@ type EthConfirmer struct { // NewEthConfirmer instantiates a new eth confirmer func NewEthConfirmer(orm ORM, ethClient evmclient.Client, config Config, keystore KeyStore, - keyStates []ethkey.State, estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, common.Hash], resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { + keyStates []ethkey.State, estimator txmgrtypes.FeeEstimator[*evmtypes.Head, gas.EvmFee, *assets.Wei, gethCommon.Hash], resumeCallback ResumeCallback, lggr logger.Logger) *EthConfirmer { ctx, cancel := context.WithCancel(context.Background()) lggr = lggr.Named("EthConfirmer") @@ -761,7 +760,7 @@ func (ec *EthConfirmer) logFieldsPreviousAttempt(attempt EthTxAttempt) []interfa func (ec *EthConfirmer) bumpGas(ctx context.Context, etx EthTx, previousAttempts []EthTxAttempt) (bumpedAttempt EthTxAttempt, err error) { // TODO: once generics are introduced at the top level struct (EthConfirmer) remove the chain-specific typings - priorAttempts := make([]txmgrtypes.PriorAttempt[gas.EvmFee, common.Hash], len(previousAttempts)) + priorAttempts := make([]txmgrtypes.PriorAttempt[gas.EvmFee, gethCommon.Hash], len(previousAttempts)) // This feels a bit useless but until we get iterators there is no other // way to cast an array of structs to an array of interfaces for i, attempt := range previousAttempts {