Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions pkg/gas/block_history_estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,28 @@ func (b *BlockHistoryEstimator) GetDynamicFee(_ context.Context, maxGasPriceWei
fee.GasTipCap = tipCap
return
}
func (b *BlockHistoryEstimator) GetMaxDynamicFee(maxGasPriceWei *assets.Wei) (fee DynamicFee, err error) {
if !b.eConfig.EIP1559DynamicFees() {
return fee, errors.New("can't get dynamic fee, EIP1559 is disabled")
}

var maxTipCap *assets.Wei
ok := b.IfStarted(func() {
maxTipCap = b.getMaxPercentileTipCap()
})
if !ok {
return fee, errors.New("BlockHistoryEstimator is not started")
}
if !b.initialFetch.Load() {
return fee, errors.New("BlockHistoryEstimator has not finished the first gas estimation yet, likely because a failure on start")
}
currentBaseFee := b.getCurrentBaseFee()
if maxTipCap == nil || currentBaseFee == nil {
return fee, errors.New("BlockHistoryEstimator: no value for latest block base fee or tip cap")
}
maxFeeCap := calcFeeCap(currentBaseFee, int(b.bhConfig.EIP1559FeeCapBufferBlocks()), maxTipCap, maxGasPriceWei)
return DynamicFee{GasFeeCap: maxFeeCap, GasTipCap: maxTipCap}, nil
}

func calcFeeCap(latestAvailableBaseFeePerGas *assets.Wei, bufferBlocks int, tipCap *assets.Wei, maxGasPriceWei *assets.Wei) (feeCap *assets.Wei) {
const maxBaseFeeIncreasePerBlock float64 = 1.125
Expand Down
31 changes: 30 additions & 1 deletion pkg/gas/fee_history_estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ type FeeHistoryEstimator struct {
priorityFeeThresholdMu sync.RWMutex
priorityFeeThreshold *assets.Wei

nextBaseFeeMu sync.RWMutex
nextBaseFee *assets.Wei

l1Oracle rollups.L1Oracle

wg *sync.WaitGroup
Expand Down Expand Up @@ -222,10 +225,23 @@ func (f *FeeHistoryEstimator) GetDynamicFee(ctx context.Context, maxPrice *asset
fee.GasTipCap = maxPrice
}
}

return
}

func (f *FeeHistoryEstimator) GetMaxDynamicFee(maxPrice *assets.Wei) (fee DynamicFee, err error) {
priorityFeeThreshold, err := f.getPriorityFeeThreshold()
if err != nil {
return fee, err
}

nextBaseFee, err := f.getNextBaseFee()
if err != nil {
return fee, err
}
maxFeeCap := nextBaseFee.AddPercentage(BaseFeeBufferPercentage).Add(priorityFeeThreshold)
return DynamicFee{GasFeeCap: maxFeeCap, GasTipCap: priorityFeeThreshold}, nil
}

// RefreshDynamicPrice uses eth_feeHistory to fetch the baseFee of the next block and the Nth maxPriorityFeePerGas percentiles
// of the past X blocks. It also fetches the highest 85th maxPriorityFeePerGas percentile of the past X blocks, which represents
// the highest percentile we're willing to pay. A buffer is added on top of the latest baseFee to catch fluctuations in the next
Expand Down Expand Up @@ -278,8 +294,12 @@ func (f *FeeHistoryEstimator) RefreshDynamicPrice() error {
priorityFeeThresholdWei = assets.NewWei(priorityFeeThreshold)
maxPriorityFeePerGas = assets.NewWei(priorityFee.Div(priorityFee, big.NewInt(nonZeroRewardsLen)))
}

// BaseFeeBufferPercentage is used as a safety to catch any fluctuations in the Base Fee during the next blocks.
maxFeePerGas := nextBaseFee.AddPercentage(BaseFeeBufferPercentage).Add(maxPriorityFeePerGas)
f.nextBaseFeeMu.Lock()
f.nextBaseFee = nextBaseFee
f.nextBaseFeeMu.Unlock()

promFeeHistoryEstimatorBaseFee.WithLabelValues(f.chainID.String()).Set(float64(nextBaseFee.Int64()))
promFeeHistoryEstimatorMaxPriorityFeePerGas.WithLabelValues(f.chainID.String()).Set(float64(maxPriorityFeePerGas.Int64()))
Expand Down Expand Up @@ -435,6 +455,15 @@ func (f *FeeHistoryEstimator) getPriorityFeeThreshold() (*assets.Wei, error) {
return f.priorityFeeThreshold, nil
}

func (f *FeeHistoryEstimator) getNextBaseFee() (*assets.Wei, error) {
f.nextBaseFeeMu.RLock()
defer f.nextBaseFeeMu.RUnlock()
if f.nextBaseFee == nil {
return f.nextBaseFee, errors.New("nextBaseFee not set")
}
return f.nextBaseFee, nil
}

func (f *FeeHistoryEstimator) Name() string { return f.logger.Name() }
func (f *FeeHistoryEstimator) L1Oracle() rollups.L1Oracle { return f.l1Oracle }
func (f *FeeHistoryEstimator) HealthReport() map[string]error { return map[string]error{f.Name(): nil} }
Expand Down
4 changes: 4 additions & 0 deletions pkg/gas/fixed_price_estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ func (f *fixedPriceEstimator) L1Oracle() rollups.L1Oracle {
return f.l1Oracle
}

func (f *fixedPriceEstimator) GetMaxDynamicFee(maxGasPriceWei *assets.Wei) (fee DynamicFee, err error) {
return f.GetDynamicFee(context.Background(), maxGasPriceWei) // context is not used by this method
}

func (f *fixedPriceEstimator) Name() string { return f.lggr.Name() }
func (f *fixedPriceEstimator) Ready() error { return nil }
func (f *fixedPriceEstimator) HealthReport() map[string]error { return map[string]error{} }
Expand Down
56 changes: 56 additions & 0 deletions pkg/gas/mocks/evm_estimator.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 83 additions & 0 deletions pkg/gas/mocks/evm_fee_estimator.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions pkg/gas/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type EvmFeeEstimator interface {

// GetMaxCost returns the total value = max price x fee units + transferred value
GetMaxCost(ctx context.Context, amount assets.Eth, calldata []byte, feeLimit uint64, maxFeePrice *assets.Wei, fromAddress, toAddress *common.Address, opts ...fees.Opt) (*big.Int, error)
GetMaxFee(ctx context.Context, calldata []byte, feeLimit uint64, maxFeePrice *assets.Wei, fromAddress, toAddress *common.Address, opts ...fees.Opt) (fee EvmFee, estimatedFeeLimit uint64, err error)
}

type feeEstimatorClient interface {
Expand Down Expand Up @@ -160,6 +161,9 @@ type EvmEstimator interface {
// 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, maxGasPriceWei *assets.Wei) (fee DynamicFee, err error)
// GetMaxDynamicFee Calculates the maximum gas fee for gas for EIP1559 transactions
// maxGasPriceWei parameter is the highest possible gas fee cap that the function will return
GetMaxDynamicFee(maxGasPriceWei *assets.Wei) (fee DynamicFee, err error)
// BumpDynamicFee Increases gas price and/or limit for non-EIP1559 transactions
// if the bumped gas fee or tip caps are greater than maxGasPriceWei, the method returns an error
// attempts must:
Expand Down Expand Up @@ -318,6 +322,29 @@ func (e *evmFeeEstimator) GetMaxCost(ctx context.Context, amount assets.Eth, cal
return amountWithFees, nil
}

func (e *evmFeeEstimator) GetMaxFee(ctx context.Context, calldata []byte, feeLimit uint64, maxFeePrice *assets.Wei, fromAddress, toAddress *common.Address, opts ...fees.Opt) (fee EvmFee, estimatedFeeLimit uint64, err error) {
var chainSpecificFeeLimit uint64
if e.EIP1559Enabled {
var dynamicFee DynamicFee
dynamicFee, err = e.EvmEstimator.GetMaxDynamicFee(maxFeePrice)
if err != nil {
return
}
fee.GasFeeCap = dynamicFee.GasFeeCap
fee.GasTipCap = dynamicFee.GasTipCap
chainSpecificFeeLimit = feeLimit
} else {
// For legacy fees assume the chain doesn't have a mempool and there is no priority, so fetching the legacy gas will have the same effect.
fee.GasPrice, chainSpecificFeeLimit, err = e.EvmEstimator.GetLegacyGas(ctx, calldata, feeLimit, maxFeePrice, opts...)
if err != nil {
return
}
}

estimatedFeeLimit, err = e.estimateFeeLimit(ctx, chainSpecificFeeLimit, calldata, fromAddress, toAddress)
return fee, estimatedFeeLimit, err
}

func (e *evmFeeEstimator) BumpFee(ctx context.Context, originalFee EvmFee, feeLimit uint64, maxFeePrice *assets.Wei, attempts []EvmPriorAttempt) (bumpedFee EvmFee, chainSpecificFeeLimit uint64, err error) {
// validate only 1 fee type is present
if (!originalFee.ValidDynamic() && originalFee.GasPrice == nil) || (originalFee.ValidDynamic() && originalFee.GasPrice != nil) {
Expand Down
5 changes: 5 additions & 0 deletions pkg/gas/suggested_price_estimator.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ func (*SuggestedPriceEstimator) GetDynamicFee(_ context.Context, _ *assets.Wei)
return
}

func (*SuggestedPriceEstimator) GetMaxDynamicFee(_ *assets.Wei) (fee DynamicFee, err error) {
err = pkgerrors.New("dynamic fees are not implemented for this estimator")
return
}

func (*SuggestedPriceEstimator) BumpDynamicFee(_ context.Context, _ DynamicFee, _ *assets.Wei, _ []EvmPriorAttempt) (bumped DynamicFee, err error) {
err = pkgerrors.New("dynamic fees are not implemented for this estimator")
return
Expand Down
Loading
Loading