-
Notifications
You must be signed in to change notification settings - Fork 173
feat(zetaclient)!: Add support for EIP-1559 gas fees #2634
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
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
0192456
Add Gas struct
0ad4145
Add EIP-1559 fees
3f953ba
Update changelog
93c930c
Merge branch 'develop' into feat/zetaclient/eip-1559
swift1337 dad95a5
Add test cases for legacy vs dynamicFee txs
fae68d5
Fix typo; Add E2E coverage
d8146f1
Address PR comments
4d6ed2c
Address PR comments
2260925
Use gasFeeCap formula
1fe1adb
Revert "Use gasFeeCap formula"
086c9af
Address PR comments
de3e3ef
Merge branch 'develop' into feat/zetaclient/eip-1559
swift1337 fdc20ad
Fix e2e upgrade tests
4eb1822
Merge branch 'feat/zetaclient/eip-1559' of github.com:zeta-chain/node…
7870420
Merge branch 'develop' into feat/zetaclient/eip-1559
swift1337 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| package signer | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "math/big" | ||
|
|
||
| "github.com/pkg/errors" | ||
| "github.com/rs/zerolog" | ||
|
|
||
| "github.com/zeta-chain/zetacore/x/crosschain/types" | ||
| ) | ||
|
|
||
| const ( | ||
| minGasLimit = 100_000 | ||
| maxGasLimit = 1_000_000 | ||
| ) | ||
|
|
||
| // Gas represents gas parameters for EVM transactions. | ||
| // | ||
| // This is pretty interesting because all EVM chains now support EIP-1559, but some chains do it in a specific way | ||
| // https://eips.ethereum.org/EIPS/eip-1559 | ||
| // https://www.blocknative.com/blog/eip-1559-fees | ||
| // https://github.com/bnb-chain/BEPs/blob/master/BEPs/BEP226.md (tl;dr: baseFee is always zero) | ||
| // | ||
| // However, this doesn't affect tx creation nor broadcasting | ||
| type Gas struct { | ||
| Limit uint64 | ||
|
|
||
| // This is a "total" gasPrice per 1 unit of gas. | ||
| // GasPrice for pre EIP-1559 transactions or maxFeePerGas for EIP-1559. | ||
| Price *big.Int | ||
|
|
||
| // PriorityFee a fee paid directly to validators for EIP-1559. | ||
| PriorityFee *big.Int | ||
| } | ||
|
|
||
| func (g Gas) validate() error { | ||
| switch { | ||
| case g.Limit == 0: | ||
| return errors.New("gas limit is zero") | ||
| case g.Price == nil: | ||
| return errors.New("max fee per unit is nil") | ||
| case g.PriorityFee == nil: | ||
| return errors.New("priority fee per unit is nil") | ||
swift1337 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| case g.Price.Cmp(g.PriorityFee) == -1: | ||
| return fmt.Errorf( | ||
| "max fee per unit (%d) is less than priority fee per unit (%d)", | ||
| g.Price.Int64(), | ||
| g.PriorityFee.Int64(), | ||
| ) | ||
| default: | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| // isLegacy determines whether the gas is meant for LegacyTx{} (pre EIP-1559) | ||
| // or DynamicFeeTx{} (post EIP-1559). | ||
| // | ||
| // Returns true if priority fee is <= 0. | ||
| func (g Gas) isLegacy() bool { | ||
| return g.PriorityFee.Sign() < 1 | ||
| } | ||
|
|
||
| func gasFromCCTX(cctx *types.CrossChainTx, logger zerolog.Logger) (Gas, error) { | ||
| var ( | ||
| params = cctx.GetCurrentOutboundParam() | ||
| limit = params.GasLimit | ||
| ) | ||
|
|
||
| switch { | ||
| case limit < minGasLimit: | ||
| limit = minGasLimit | ||
| logger.Warn(). | ||
| Uint64("cctx.initial_gas_limit", params.GasLimit). | ||
| Uint64("cctx.gas_limit", limit). | ||
| Msgf("Gas limit is too low. Setting to the minimum (%d)", minGasLimit) | ||
| case limit > maxGasLimit: | ||
| limit = maxGasLimit | ||
| logger.Warn(). | ||
| Uint64("cctx.initial_gas_limit", params.GasLimit). | ||
| Uint64("cctx.gas_limit", limit). | ||
| Msgf("Gas limit is too high; Setting to the maximum (%d)", maxGasLimit) | ||
| } | ||
|
|
||
| gasPrice, err := bigIntFromString(params.GasPrice) | ||
| if err != nil { | ||
| return Gas{}, errors.Wrap(err, "unable to parse gasPrice") | ||
| } | ||
|
|
||
| priorityFee, err := bigIntFromString(params.GasPriorityFee) | ||
| switch { | ||
| case err != nil: | ||
| return Gas{}, errors.Wrap(err, "unable to parse priorityFee") | ||
| case gasPrice.Cmp(priorityFee) == -1: | ||
| return Gas{}, fmt.Errorf("gasPrice (%d) is less than priorityFee (%d)", gasPrice.Int64(), priorityFee.Int64()) | ||
| } | ||
|
|
||
| return Gas{ | ||
| Limit: limit, | ||
| Price: gasPrice, | ||
| PriorityFee: priorityFee, | ||
| }, nil | ||
| } | ||
|
|
||
| func bigIntFromString(s string) (*big.Int, error) { | ||
| if s == "" || s == "0" { | ||
| return big.NewInt(0), nil | ||
| } | ||
|
|
||
| v, ok := new(big.Int).SetString(s, 10) | ||
| if !ok { | ||
| return nil, fmt.Errorf("unable to parse %q as big.Int", s) | ||
| } | ||
|
|
||
| if v.Sign() == -1 { | ||
| return nil, fmt.Errorf("big.Int is negative: %d", v.Int64()) | ||
| } | ||
|
|
||
| return v, nil | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| package signer | ||
|
|
||
| import ( | ||
| "math/big" | ||
| "testing" | ||
|
|
||
| "github.com/rs/zerolog" | ||
| "github.com/stretchr/testify/assert" | ||
| "github.com/zeta-chain/zetacore/x/crosschain/types" | ||
| ) | ||
|
|
||
| func TestGasFromCCTX(t *testing.T) { | ||
| logger := zerolog.New(zerolog.NewTestWriter(t)) | ||
|
|
||
| makeCCTX := func(gasLimit uint64, price, priorityFee string) *types.CrossChainTx { | ||
| cctx := getCCTX(t) | ||
| cctx.GetOutboundParams()[0].GasLimit = gasLimit | ||
| cctx.GetOutboundParams()[0].GasPrice = price | ||
| cctx.GetOutboundParams()[0].GasPriorityFee = priorityFee | ||
|
|
||
| return cctx | ||
| } | ||
|
|
||
| for _, tt := range []struct { | ||
| name string | ||
| cctx *types.CrossChainTx | ||
| errorContains string | ||
| assert func(t *testing.T, g Gas) | ||
| }{ | ||
| { | ||
| name: "legacy: gas is too low", | ||
| cctx: makeCCTX(minGasLimit-200, gwei(2).String(), ""), | ||
| assert: func(t *testing.T, g Gas) { | ||
| assert.True(t, g.isLegacy()) | ||
| assertGasEquals(t, Gas{ | ||
| Limit: minGasLimit, | ||
| PriorityFee: gwei(0), | ||
| Price: gwei(2), | ||
| }, g) | ||
| }, | ||
| }, | ||
| { | ||
| name: "london: gas is too low", | ||
| cctx: makeCCTX(minGasLimit-200, gwei(2).String(), gwei(1).String()), | ||
| assert: func(t *testing.T, g Gas) { | ||
| assert.False(t, g.isLegacy()) | ||
| assertGasEquals(t, Gas{ | ||
| Limit: minGasLimit, | ||
| Price: gwei(2), | ||
| PriorityFee: gwei(1), | ||
| }, g) | ||
| }, | ||
| }, | ||
| { | ||
| name: "pre London gas logic", | ||
| cctx: makeCCTX(minGasLimit+100, gwei(3).String(), ""), | ||
| assert: func(t *testing.T, g Gas) { | ||
| assert.True(t, g.isLegacy()) | ||
| assertGasEquals(t, Gas{ | ||
| Limit: 100_100, | ||
| Price: gwei(3), | ||
| PriorityFee: gwei(0), | ||
| }, g) | ||
| }, | ||
| }, | ||
| { | ||
| name: "post London gas logic", | ||
| cctx: makeCCTX(minGasLimit+200, gwei(4).String(), gwei(1).String()), | ||
| assert: func(t *testing.T, g Gas) { | ||
| assert.False(t, g.isLegacy()) | ||
| assertGasEquals(t, Gas{ | ||
| Limit: 100_200, | ||
| Price: gwei(4), | ||
| PriorityFee: gwei(1), | ||
| }, g) | ||
| }, | ||
| }, | ||
| { | ||
| name: "gas is too high, force to the ceiling", | ||
| cctx: makeCCTX(maxGasLimit+200, gwei(4).String(), gwei(1).String()), | ||
| assert: func(t *testing.T, g Gas) { | ||
| assert.False(t, g.isLegacy()) | ||
| assertGasEquals(t, Gas{ | ||
| Limit: maxGasLimit, | ||
| Price: gwei(4), | ||
| PriorityFee: gwei(1), | ||
| }, g) | ||
| }, | ||
| }, | ||
| { | ||
| name: "priority fee is invalid", | ||
| cctx: makeCCTX(123_000, gwei(4).String(), "oopsie"), | ||
| errorContains: "unable to parse priorityFee", | ||
| }, | ||
| { | ||
| name: "priority fee is negative", | ||
| cctx: makeCCTX(123_000, gwei(4).String(), "-1"), | ||
| errorContains: "unable to parse priorityFee: big.Int is negative", | ||
| }, | ||
| { | ||
| name: "gasPrice is less than priorityFee", | ||
| cctx: makeCCTX(123_000, gwei(4).String(), gwei(5).String()), | ||
| errorContains: "gasPrice (4000000000) is less than priorityFee (5000000000)", | ||
| }, | ||
| { | ||
| name: "gasPrice is invalid", | ||
| cctx: makeCCTX(123_000, "hello", gwei(5).String()), | ||
| errorContains: "unable to parse gasPrice", | ||
| }, | ||
| } { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| g, err := gasFromCCTX(tt.cctx, logger) | ||
| if tt.errorContains != "" { | ||
| assert.ErrorContains(t, err, tt.errorContains) | ||
| return | ||
| } | ||
|
|
||
| assert.NoError(t, err) | ||
| assert.NoError(t, g.validate()) | ||
| tt.assert(t, g) | ||
| }) | ||
| } | ||
|
|
||
| t.Run("empty priority fee", func(t *testing.T) { | ||
| gas := Gas{ | ||
| Limit: 123_000, | ||
| Price: gwei(4), | ||
| PriorityFee: nil, | ||
| } | ||
|
|
||
| assert.Error(t, gas.validate()) | ||
| }) | ||
| } | ||
|
|
||
| func assertGasEquals(t *testing.T, expected, actual Gas) { | ||
| assert.Equal(t, int64(expected.Limit), int64(actual.Limit), "gas limit") | ||
| assert.Equal(t, expected.Price.Int64(), actual.Price.Int64(), "max fee per unit") | ||
| assert.Equal(t, expected.PriorityFee.Int64(), actual.PriorityFee.Int64(), "priority fee per unit") | ||
| } | ||
|
|
||
| func gwei(i int64) *big.Int { | ||
| const g = 1_000_000_000 | ||
| return big.NewInt(i * g) | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.