Skip to content

Commit 912f58a

Browse files
authored
Merge pull request #69 from datachainlab/add-support-for-price-bump-when-tx-type-auto
Add support for price bump when tx_type="auto"
2 parents 8716271 + 5c4ff80 commit 912f58a

File tree

3 files changed

+244
-45
lines changed

3 files changed

+244
-45
lines changed

pkg/relay/ethereum/client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type IChainClient interface {
5252
ethereum.ChainReader
5353
ethereum.GasPricer
5454
ethereum.FeeHistoryReader
55+
ethereum.GasPricer1559
5556

5657
GetMinimumRequiredFee(ctx context.Context, address common.Address, nonce uint64, priceBump uint64) (*txpool.RPCTransaction, *big.Int, *big.Int, error)
5758
}

pkg/relay/ethereum/gas.go

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"github.com/ethereum/go-ethereum/common"
1212
)
1313

14+
const basefeeWiggleMultiplier = 2
15+
1416
type GasFeeCalculator struct {
1517
client IChainClient
1618
config *ChainConfig
@@ -35,15 +37,9 @@ func (m *GasFeeCalculator) Apply(ctx context.Context, txOpts *bind.TransactOpts)
3537
}
3638
switch m.config.TxType {
3739
case TxTypeLegacy:
38-
gasPrice, err := m.client.SuggestGasPrice(ctx)
40+
gasPrice, err := m.calculateGasPrice(ctx, oldTx, minFeeCap)
3941
if err != nil {
40-
return fmt.Errorf("failed to suggest gas price: %v", err)
41-
}
42-
if oldTx != nil && oldTx.GasPrice != nil && oldTx.GasPrice.ToInt().Cmp(gasPrice) > 0 {
43-
return fmt.Errorf("old tx's gasPrice(%v) is higher than suggestion(%v)", oldTx.GasPrice.ToInt(), gasPrice)
44-
}
45-
if gasPrice.Cmp(minFeeCap) < 0 {
46-
gasPrice = minFeeCap
42+
return fmt.Errorf("failed to calculate gas price: %v", err)
4743
}
4844
txOpts.GasPrice = gasPrice
4945
return nil
@@ -59,22 +55,14 @@ func (m *GasFeeCalculator) Apply(ctx context.Context, txOpts *bind.TransactOpts)
5955
m.config.DynamicTxGasConfig.BaseFeeRate.Mul(gasFeeCap)
6056
gasFeeCap.Add(gasFeeCap, gasTipCap)
6157

62-
if oldTx != nil && oldTx.GasFeeCap != nil && oldTx.GasTipCap != nil {
63-
if oldTx.GasFeeCap.ToInt().Cmp(gasFeeCap) >= 0 && oldTx.GasTipCap.ToInt().Cmp(gasTipCap) >= 0 {
64-
return fmt.Errorf("old tx's gasFeeCap(%v) and gasTipCap(%v) are greater than or equal to suggestion(%v, %v)", oldTx.GasFeeCap.ToInt(), oldTx.GasTipCap.ToInt(), gasFeeCap, gasTipCap)
65-
}
58+
gasTipCap, gasFeeCap, err = m.applyMinGasCaps(oldTx, gasTipCap, gasFeeCap, minTipCap, minFeeCap)
59+
if err != nil {
60+
return fmt.Errorf("failed to apply min gas caps: %v", err)
6661
}
6762

68-
if gasTipCap.Cmp(minTipCap) < 0 {
69-
gasTipCap = minTipCap
70-
}
7163
if l := m.config.DynamicTxGasConfig.GetLimitPriorityFeePerGas(); l.Sign() > 0 && gasTipCap.Cmp(l) > 0 {
7264
gasTipCap = l
7365
}
74-
75-
if gasFeeCap.Cmp(minFeeCap) < 0 {
76-
gasFeeCap = minFeeCap
77-
}
7866
if l := m.config.DynamicTxGasConfig.GetLimitFeePerGas(); l.Sign() > 0 && gasFeeCap.Cmp(l) > 0 {
7967
gasFeeCap = l
8068
}
@@ -85,11 +73,62 @@ func (m *GasFeeCalculator) Apply(ctx context.Context, txOpts *bind.TransactOpts)
8573
txOpts.GasFeeCap = gasFeeCap
8674
txOpts.GasTipCap = gasTipCap
8775
return nil
76+
case TxTypeAuto:
77+
// Calculate gas options in the same way as bind.BoundContract.transact
78+
head, err := m.client.HeaderByNumber(ctx, nil)
79+
if err != nil {
80+
return fmt.Errorf("failed to get latest header: %v", err)
81+
}
82+
83+
if head.BaseFee == nil {
84+
gasPrice, err := m.calculateGasPrice(ctx, oldTx, minFeeCap)
85+
if err != nil {
86+
return fmt.Errorf("failed to calculate gas price: %v", err)
87+
}
88+
txOpts.GasPrice = gasPrice
89+
return nil
90+
} else {
91+
gasTipCap, err := m.client.SuggestGasTipCap(ctx)
92+
if err != nil {
93+
return fmt.Errorf("failed to suggest gas tip cap: %v", err)
94+
}
95+
gasFeeCap := new(big.Int).Add(
96+
gasTipCap,
97+
new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)),
98+
)
99+
100+
gasTipCap, gasFeeCap, err = m.applyMinGasCaps(oldTx, gasTipCap, gasFeeCap, minTipCap, minFeeCap)
101+
if err != nil {
102+
return fmt.Errorf("failed to apply min gas caps: %v", err)
103+
}
104+
105+
txOpts.GasFeeCap = gasFeeCap
106+
txOpts.GasTipCap = gasTipCap
107+
return nil
108+
}
88109
default:
89-
return nil
110+
panic("unsupported tx type")
90111
}
91112
}
92113

114+
func (m *GasFeeCalculator) calculateGasPrice(ctx context.Context, oldTx *txpool.RPCTransaction, minFeeCap *big.Int) (*big.Int, error) {
115+
gasPrice, err := m.client.SuggestGasPrice(ctx)
116+
if err != nil {
117+
return nil, fmt.Errorf("failed to suggest gas price: %v", err)
118+
}
119+
if oldTx != nil && oldTx.GasPrice != nil && oldTx.GasPrice.ToInt().Cmp(gasPrice) > 0 {
120+
// Since the old tx's gas price is already higher than the suggested value,
121+
// the gas price is not the reason the old tx has not been processed.
122+
// To avoid raising it indefinitely, return an error.
123+
return nil, fmt.Errorf("old tx's gasPrice(%v) is higher than suggestion(%v)", oldTx.GasPrice.ToInt(), gasPrice)
124+
}
125+
if gasPrice.Cmp(minFeeCap) < 0 {
126+
gasPrice = minFeeCap
127+
}
128+
129+
return gasPrice, nil
130+
}
131+
93132
func (m *GasFeeCalculator) feeHistory(ctx context.Context) (*big.Int, *big.Int, error) {
94133
rewardPercentile := float64(m.config.DynamicTxGasConfig.FeeHistoryRewardPercentile)
95134
maxRetry := m.config.DynamicTxGasConfig.MaxRetryForFeeHistory
@@ -125,3 +164,33 @@ func getFeeInfo(v *ethereum.FeeHistory) (*big.Int, *big.Int, bool) {
125164
baseFee := v.BaseFee[0]
126165
return gasTipCap, baseFee, true
127166
}
167+
168+
func (m *GasFeeCalculator) applyMinGasCaps(
169+
oldTx *txpool.RPCTransaction,
170+
gasTipCap *big.Int,
171+
gasFeeCap *big.Int,
172+
minTipCap *big.Int,
173+
minFeeCap *big.Int,
174+
) (*big.Int, *big.Int, error) {
175+
if oldTx != nil && oldTx.GasFeeCap != nil && oldTx.GasTipCap != nil {
176+
if oldTx.GasFeeCap.ToInt().Cmp(gasFeeCap) >= 0 && oldTx.GasTipCap.ToInt().Cmp(gasTipCap) >= 0 {
177+
// Since the old tx's gas parameters are already higher than the suggested values,
178+
// the gas parameters are not the reason the old tx has not been processed.
179+
// To avoid raising them indefinitely, return an error.
180+
return nil, nil, fmt.Errorf("old tx's gasFeeCap(%v) and gasTipCap(%v) are greater than or equal to suggestion(%v, %v)", oldTx.GasFeeCap.ToInt(), oldTx.GasTipCap.ToInt(), gasFeeCap, gasTipCap)
181+
}
182+
}
183+
184+
if gasTipCap.Cmp(minTipCap) < 0 {
185+
gasTipCap = minTipCap
186+
}
187+
if gasFeeCap.Cmp(minFeeCap) < 0 {
188+
gasFeeCap = minFeeCap
189+
}
190+
191+
if gasFeeCap.Cmp(gasTipCap) < 0 {
192+
return nil, nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
193+
}
194+
195+
return gasTipCap, gasFeeCap, nil
196+
}

0 commit comments

Comments
 (0)