Skip to content

Commit dbf8c5e

Browse files
fix(transaction): improve legacy gasPrice to EIP-1559 dynamic fee (#5225)
1 parent ec5ff0d commit dbf8c5e

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

pkg/transaction/wrapped/fee.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,44 @@ var (
2020
ErrEIP1559NotSupported = errors.New("network does not appear to support EIP-1559 (no baseFee)")
2121
)
2222

23-
// SuggestedFeeAndTip calculates the recommended gasFeeCap and gasTipCap for a transaction.
23+
// SuggestedFeeAndTip calculates the recommended gasFeeCap (maxFeePerGas) and gasTipCap (maxPriorityFeePerGas) for a transaction.
24+
// If gasPrice is provided (legacy mode):
25+
// - On EIP-1559 networks: gasFeeCap = gasPrice; gasTipCap = max(gasPrice - baseFee, minimumTip) to respect the total cap while enforcing a tip floor where possible.
26+
// - On pre-EIP-1559 networks: returns (gasPrice, gasPrice) for legacy transaction compatibility.
27+
//
28+
// If gasPrice is nil: Uses suggested tip with optional boost, enforces minimum, and sets gasFeeCap = 2 * baseFee + gasTipCap.
2429
func (b *wrappedBackend) SuggestedFeeAndTip(ctx context.Context, gasPrice *big.Int, boostPercent int) (*big.Int, *big.Int, error) {
2530
if gasPrice != nil {
26-
// 1. gasFeeCap: The absolute maximum price per gas does not exceed the user's specified price.
27-
// 2. gasTipCap: The entire amount (gasPrice - baseFee) can be used as a priority fee.
28-
return new(big.Int).Set(gasPrice), new(big.Int).Set(gasPrice), nil
31+
latestBlockHeader, err := b.backend.HeaderByNumber(ctx, nil)
32+
if err != nil {
33+
return nil, nil, fmt.Errorf("failed to get latest block header: %w", err)
34+
}
35+
if latestBlockHeader == nil || latestBlockHeader.BaseFee == nil {
36+
return new(big.Int).Set(gasPrice), new(big.Int).Set(gasPrice), nil
37+
}
38+
39+
baseFee := latestBlockHeader.BaseFee
40+
if gasPrice.Cmp(baseFee) < 0 {
41+
return nil, nil, fmt.Errorf("specified gas price %s is below current base fee %s", gasPrice, baseFee)
42+
}
43+
44+
// nominal tip = gasPrice - baseFee
45+
gasTipCap := new(big.Int).Sub(gasPrice, baseFee)
46+
gasFeeCap := new(big.Int).Set(gasPrice)
47+
48+
return gasFeeCap, gasTipCap, nil
2949
}
3050

3151
gasTipCap, err := b.backend.SuggestGasTipCap(ctx)
3252
if err != nil {
3353
return nil, nil, fmt.Errorf("failed to suggest gas tip cap: %w", err)
3454
}
55+
gasTipCap = new(big.Int).Set(gasTipCap)
3556

3657
if boostPercent != 0 {
58+
if boostPercent < 0 {
59+
return nil, nil, fmt.Errorf("negative boostPercent (%d) not allowed", boostPercent)
60+
}
3761
// multiplier: 100 + boostPercent (e.g., 110 for 10% boost)
3862
multiplier := new(big.Int).Add(big.NewInt(int64(percentageDivisor)), big.NewInt(int64(boostPercent)))
3963
// gasTipCap = gasTipCap * (100 + boostPercent) / 100
@@ -53,7 +77,7 @@ func (b *wrappedBackend) SuggestedFeeAndTip(ctx context.Context, gasPrice *big.I
5377
return nil, nil, ErrEIP1559NotSupported
5478
}
5579

56-
// EIP-1559: gasFeeCap = (2 * baseFee) + gasTipCap
80+
// gasFeeCap = (2 * baseFee) + gasTipCap
5781
gasFeeCap := new(big.Int).Mul(latestBlockHeader.BaseFee, big.NewInt(int64(baseFeeMultiplier)))
5882
gasFeeCap.Add(gasFeeCap, gasTipCap)
5983

pkg/transaction/wrapped/fee_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ func TestSuggestedFeeAndTip(t *testing.T) {
4343
wantGasFeeCap: big.NewInt(1000),
4444
wantGasTipCap: big.NewInt(1000),
4545
},
46+
{
47+
name: "with gas price and base fee",
48+
gasPrice: big.NewInt(1000),
49+
mockHeader: &types.Header{BaseFee: baseFee},
50+
wantGasFeeCap: big.NewInt(1000),
51+
wantGasTipCap: big.NewInt(900),
52+
},
4653
{
4754
name: "suggest tip error",
4855
mockSuggestGasErr: errors.New("suggest tip error"),

0 commit comments

Comments
 (0)