-
Notifications
You must be signed in to change notification settings - Fork 70
support fixed gas fee cap and tip cap #868
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| +1 −1 | Dockerfile | |
| +5 −8 | core/state_transition.go | |
| +2 −1 | core/token_gas.go | |
| +1 −1 | core/tx_list.go | |
| +1 −1 | core/types/receipt.go | |
| +9 −12 | core/types/token_fee.go | |
| +2 −0 | eth/catalyst/api_types.go | |
| +4 −0 | eth/catalyst/gen_l2blockparams.go | |
| +5 −1 | eth/catalyst/l2_api.go | |
| +3 −3 | eth/catalyst/l2_api_test.go | |
| +2 −1 | ethclient/authclient/engine.go | |
| +0 −1 | internal/ethapi/transaction_args.go | |
| +2 −2 | miner/miner.go | |
| +0 −2 | params/config.go | |
| +2 −2 | params/version.go | |
| +19 −7 | rollup/fees/rate.go | |
| +6 −6 | rollup/fees/token_info.go | |
| +1 −1 | rollup/fees/token_transfer.go |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -96,18 +96,25 @@ func (s *Signer) CreateAndSignTx( | |||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("failed to get nonce: %w", err) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Get gas tip cap | ||||||||||||||||||||||||||||||||||||||
| // Get gas tip cap (dynamic, then apply cap if configured) | ||||||||||||||||||||||||||||||||||||||
| tip, err := client.GetClient().SuggestGasTipCap(ctx) | ||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("failed to get gas tip cap: %w", err) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| if maxTip := client.GetMaxGasTipCap(); maxTip != nil { | ||||||||||||||||||||||||||||||||||||||
| if tip.Cmp(maxTip) > 0 { | ||||||||||||||||||||||||||||||||||||||
| log.Debug("Applying gas tip cap limit", "dynamic", tip, "cap", maxTip) | ||||||||||||||||||||||||||||||||||||||
| tip = maxTip | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Get base fee from latest block | ||||||||||||||||||||||||||||||||||||||
| head, err := client.GetClient().HeaderByNumber(ctx, nil) | ||||||||||||||||||||||||||||||||||||||
| if err != nil { | ||||||||||||||||||||||||||||||||||||||
| return nil, fmt.Errorf("failed to get block header: %w", err) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Calculate dynamic gas fee cap | ||||||||||||||||||||||||||||||||||||||
| var gasFeeCap *big.Int | ||||||||||||||||||||||||||||||||||||||
| if head.BaseFee != nil { | ||||||||||||||||||||||||||||||||||||||
| gasFeeCap = new(big.Int).Add( | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -118,6 +125,14 @@ func (s *Signer) CreateAndSignTx( | |||||||||||||||||||||||||||||||||||||
| gasFeeCap = new(big.Int).Set(tip) | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Apply gas fee cap limit if configured | ||||||||||||||||||||||||||||||||||||||
| if maxFeeCap := client.GetMaxGasFeeCap(); maxFeeCap != nil { | ||||||||||||||||||||||||||||||||||||||
| if gasFeeCap.Cmp(maxFeeCap) > 0 { | ||||||||||||||||||||||||||||||||||||||
| log.Debug("Applying gas fee cap limit", "dynamic", gasFeeCap, "cap", maxFeeCap) | ||||||||||||||||||||||||||||||||||||||
| gasFeeCap = maxFeeCap | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+129
to
+134
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same aliasing issue for
Proposed fix (apply to both sign.go and tx_manager.go) // Apply gas fee cap limit if configured
if maxFeeCap := client.GetMaxGasFeeCap(); maxFeeCap != nil {
if gasFeeCap.Cmp(maxFeeCap) > 0 {
log.Debug("Applying gas fee cap limit", "dynamic", gasFeeCap, "cap", maxFeeCap)
- gasFeeCap = maxFeeCap
+ gasFeeCap = new(big.Int).Set(maxFeeCap)
}
}
+
+ // Ensure gasTipCap <= gasFeeCap (EIP-1559 invariant)
+ if tip.Cmp(gasFeeCap) > 0 {
+ log.Debug("Clamping tip to gasFeeCap", "tip", tip, "gasFeeCap", gasFeeCap)
+ tip = new(big.Int).Set(gasFeeCap)
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| // Estimate gas | ||||||||||||||||||||||||||||||||||||||
| gas, err := client.GetClient().EstimateGas(ctx, ethereum.CallMsg{ | ||||||||||||||||||||||||||||||||||||||
| From: from, | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -85,6 +85,10 @@ type Config struct { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LogFileMaxSize int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LogFileMaxAge int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LogCompress bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Gas fee caps (optional - if set, use as max cap) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GasFeeCap *uint64 // Max gas fee cap in wei (nil means no cap) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GasTipCap *uint64 // Max gas tip cap in wei (nil means no cap) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // LoadConfig loads configuration from cli.Context | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -112,6 +116,16 @@ func LoadConfig(ctx *cli.Context) (*Config, error) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LogCompress: ctx.Bool(flags.LogCompressFlag.Name), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Gas fee caps (only set if flag is explicitly provided) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ctx.IsSet(flags.GasFeeCapFlag.Name) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| v := ctx.Uint64(flags.GasFeeCapFlag.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cfg.GasFeeCap = &v | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ctx.IsSet(flags.GasTipCapFlag.Name) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| v := ctx.Uint64(flags.GasTipCapFlag.Name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cfg.GasTipCap = &v | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+119
to
+127
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add validation: reject zero values and enforce Two gaps:
Both are easy to catch here at config load time. Proposed fix // Gas fee caps (only set if flag is explicitly provided)
if ctx.IsSet(flags.GasFeeCapFlag.Name) {
v := ctx.Uint64(flags.GasFeeCapFlag.Name)
+ if v == 0 {
+ return nil, fmt.Errorf("gas-fee-cap must be greater than 0 when set")
+ }
cfg.GasFeeCap = &v
}
if ctx.IsSet(flags.GasTipCapFlag.Name) {
v := ctx.Uint64(flags.GasTipCapFlag.Name)
+ if v == 0 {
+ return nil, fmt.Errorf("gas-tip-cap must be greater than 0 when set")
+ }
cfg.GasTipCap = &v
}
+ if cfg.GasFeeCap != nil && cfg.GasTipCap != nil && *cfg.GasFeeCap < *cfg.GasTipCap {
+ return nil, fmt.Errorf("gas-fee-cap (%d) must be >= gas-tip-cap (%d)", *cfg.GasFeeCap, *cfg.GasTipCap)
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Parse token registry address (optional) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cfg.L2TokenRegistryAddr = predeploys.L2TokenRegistryAddr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ package updater | |
| import ( | ||
| "context" | ||
| "fmt" | ||
| "math/big" | ||
| "sync" | ||
| "time" | ||
|
|
||
|
|
@@ -72,6 +73,11 @@ func (m *TxManager) sendWithLocalSign(ctx context.Context, txFunc func(*bind.Tra | |
| auth := m.l2Client.GetOpts() | ||
| auth.Context = ctx | ||
|
|
||
| // Apply gas caps if configured (same logic as external sign) | ||
| if err := m.applyGasCaps(ctx, auth); err != nil { | ||
| return nil, fmt.Errorf("failed to apply gas caps: %w", err) | ||
| } | ||
|
|
||
| // First, estimate gas with GasLimit = 0 | ||
| auth.GasLimit = 0 | ||
| auth.NoSend = true | ||
|
|
@@ -193,6 +199,58 @@ func (m *TxManager) sendWithExternalSign(ctx context.Context, txFunc func(*bind. | |
| return receipt, nil | ||
| } | ||
|
|
||
| // applyGasCaps applies configured gas caps as upper limits to dynamic gas prices | ||
| // This ensures consistent behavior between local sign and external sign | ||
| func (m *TxManager) applyGasCaps(ctx context.Context, auth *bind.TransactOpts) error { | ||
| maxTipCap := m.l2Client.GetMaxGasTipCap() | ||
| maxFeeCap := m.l2Client.GetMaxGasFeeCap() | ||
|
|
||
| // If no caps configured, let bind package handle gas pricing dynamically | ||
| if maxTipCap == nil && maxFeeCap == nil { | ||
| return nil | ||
| } | ||
|
|
||
| // Get dynamic gas tip cap | ||
| tip, err := m.l2Client.GetClient().SuggestGasTipCap(ctx) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to get gas tip cap: %w", err) | ||
| } | ||
|
|
||
| // Apply tip cap limit if configured | ||
| if maxTipCap != nil && tip.Cmp(maxTipCap) > 0 { | ||
| log.Debug("Applying gas tip cap limit", "dynamic", tip, "cap", maxTipCap) | ||
| tip = new(big.Int).Set(maxTipCap) | ||
| } | ||
| auth.GasTipCap = tip | ||
|
|
||
| // Get base fee from latest block | ||
| head, err := m.l2Client.GetClient().HeaderByNumber(ctx, nil) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to get block header: %w", err) | ||
| } | ||
|
|
||
| // Calculate dynamic gas fee cap | ||
| var gasFeeCap *big.Int | ||
| if head.BaseFee != nil { | ||
| gasFeeCap = new(big.Int).Add( | ||
| tip, | ||
| new(big.Int).Mul(head.BaseFee, big.NewInt(2)), | ||
| ) | ||
| } else { | ||
| gasFeeCap = new(big.Int).Set(tip) | ||
| } | ||
|
|
||
| // Apply fee cap limit if configured | ||
| if maxFeeCap != nil && gasFeeCap.Cmp(maxFeeCap) > 0 { | ||
| log.Debug("Applying gas fee cap limit", "dynamic", gasFeeCap, "cap", maxFeeCap) | ||
| gasFeeCap = new(big.Int).Set(maxFeeCap) | ||
| } | ||
| auth.GasFeeCap = gasFeeCap | ||
|
|
||
| log.Debug("Gas caps applied", "tipCap", auth.GasTipCap, "feeCap", auth.GasFeeCap) | ||
| return nil | ||
| } | ||
|
Comment on lines
+202
to
+252
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same missing After clamping auth.GasFeeCap = gasFeeCap
+
+ // Ensure gasTipCap <= gasFeeCap (EIP-1559 invariant)
+ if auth.GasTipCap.Cmp(auth.GasFeeCap) > 0 {
+ log.Debug("Clamping tip to gasFeeCap", "tip", auth.GasTipCap, "gasFeeCap", auth.GasFeeCap)
+ auth.GasTipCap = new(big.Int).Set(auth.GasFeeCap)
+ }🤖 Prompt for AI Agents |
||
|
|
||
| // waitForReceipt waits for a transaction receipt with timeout and custom polling interval | ||
| func (m *TxManager) waitForReceipt(ctx context.Context, txHash common.Hash, timeout, pollInterval time.Duration) (*types.Receipt, error) { | ||
| deadline := time.Now().Add(timeout) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing defensive copy —
tipaliasesL2Client's internalgasTipCap.When the cap is applied,
tip = maxTipmakestippoint to the same*big.Intstored insideL2Client. Any future in-place mutation oftipwould corrupt the shared state. The parallel code intx_manager.go(line 222) correctly usesnew(big.Int).Set(maxTipCap).Proposed fix
if maxTip := client.GetMaxGasTipCap(); maxTip != nil { if tip.Cmp(maxTip) > 0 { log.Debug("Applying gas tip cap limit", "dynamic", tip, "cap", maxTip) - tip = maxTip + tip = new(big.Int).Set(maxTip) } }📝 Committable suggestion
🤖 Prompt for AI Agents