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
EIP-1559 transactions support #2013
Conversation
65ad322
to
b40086c
Compare
40d49db
to
6be85fb
Compare
if address != am.account.Address { | ||
return nil, errors.New("not authorized to sign this account") | ||
} | ||
opts, err := bind.NewKeyStoreTransactorWithChainID(am.keyStore, am.account, am.chainID) |
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.
bind.NewKeyStoreTransactorWithChainID basically does the same thing as the removed code snippet for creating a bind.TransactOpts
.
However, note that before we were configuring a EIP-155 signer, but now this method configures a signer using types.LatestSignerForChainID. The signer returned by this method will still support EIP-155 replay protection, but will also support additional transaction types - see this comment.
} | ||
|
||
// Sign a transaction. Account must be unlocked | ||
func (am *accountManager) SignTx(tx *types.Transaction) (*types.Transaction, error) { | ||
signature, err := am.keyStore.SignHash(am.account, am.signer.Hash(tx).Bytes()) | ||
signer := types.LatestSignerForChainID(am.chainID) |
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.
Note that we use types.LatestSignerForChainID
to configure a signer to match the behavior of bind.NewKeyStoreTransactorWithChainID
which we are now using to create the bind.TransactOpts
used for contract binding transactions.
@@ -80,11 +81,10 @@ func (b *backend) SendTransaction(ctx context.Context, tx *types.Transaction) er | |||
return err | |||
} | |||
|
|||
msg, err := tx.AsMessage(b.signer) | |||
sender, err := types.Sender(b.signer, tx) |
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.
types.Sender returns the address derived from the signature over the provided transaction.
@@ -383,5 +383,5 @@ func (c *StubClient) NextValidRequest(common.Address) (*big.Int, error) { return | |||
|
|||
// Governance | |||
func (c *StubClient) Vote(pollAddr ethcommon.Address, choiceID *big.Int) (*types.Transaction, error) { | |||
return &types.Transaction{}, c.Err | |||
return types.NewTx(&types.DynamicFeeTx{}), c.Err |
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.
This is the new way of creating transaction objects - we have to specify whether we are creating a dynamic fee tx (i.e. supports EIP-1559) or a legacy tx (i.e. does not support EIP-1559).
@@ -244,7 +244,6 @@ func TestTransactionManager_Replace(t *testing.T) { | |||
assert.Nil(err) | |||
expTx := types.NewTransaction(1, *stubTx.To(), stubTx.Value(), 100000, calcReplacementGasPrice(stubTx), stubTx.Data()) | |||
assert.Equal(tx.Hash(), expTx.Hash()) | |||
assert.Equal(tx, expTx) |
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.
I think these assertions for the actual transactions themselves needed to be removed because the transaction type contains a time field (used for spam avoidance use cases in the mem pool) that is set when a transaction is created which means that this internal field will be different for transactions created at different times.
eth/backend.go
Outdated
// In the future, eth_maxPriorityFeePerGas can be replaced with an eth_feeHistory based algorithm (see https://github.com/ethereum/go-ethereum/issues/23479). | ||
// For now, if the provider does not support eth_maxPriorityFeePerGas (i.e. not geth), then we calculate the priority fee as | ||
// eth_gasPrice - baseFee. | ||
if err.Error() == "Method not found" { |
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.
This logic should only be executed if the node is a) not using Infura/Alchemy [1] and b) not using geth.
The use of the fee history API would be better. This is just a temporary patch so that the node doesn't just completely fail if a) and b) mentioned above do not hold.
[1] I'm not sure if there are other hosted services to consider here, but at least these two support the eth_maxPriorityFeePerGas RPC method.
// Note: If the suggested gas price is lower than the bumped gas price because market gas prices have dropped | ||
// since the time of the original tx submission we cannot use the lower suggested gas price and we still need to use | ||
// the bumped gas price in order to properly replace a still pending tx | ||
if suggestedGasPrice.Cmp(gasPrice) == 1 { |
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.
Note that the node will no longer check if the bumped gas price is less than the suggested gas price. Removed to simplify a few things (avoid breaking up suggested gas price into priority fee, base fee in order to set the gas fee fields on the transaction) and because seemed like the situation is pretty rare and the current replacement behavior should largely cover it by allowing for another bump if the first is insufficient.
03f8634
to
b7f0f38
Compare
Successful transaction replacements on Rinkeby:
Ran this test by setting |
Holding off on a merge here because I discovered a few remaining issues with this PR:
|
For now, this is fixed in e73a5a8. The one downside of the change in that commit is that if the value returned by eth_gasPrice is lower than the sum of eth_maxPriorityFeePerGas + the base fee then a transaction could go through with a priority fee + base fee that exceeds the max gas price. This is possible because the max gas price check is run via
This is fixed by 31eb840 (squashed a fixup commit in).
I opened a PR to fix this in go-ethereum here. For now, while that PR is unmerged, I've switched the go-ethereum dependency to a fork with the fix in 63a8a2d. |
39a763e
to
e73a5a8
Compare
Fallback logic is needed right now if the JSON-RPC provider does not support the eth_maxPriorityFeePerGas RPC method.
Mention gas price is treated as priority fee + base fee now
To fix TransactOpts gas fee overwrite bug
e73a5a8
to
4ae526b
Compare
What does this pull request do? Explain your changes. (required)
This PR adds support for EIP-1559 transactions.
Under the hood, all transactions submitted by the node will use the new EIP-1559 gas fee fields. Refer to #1972 and the linked resources there for an overview of the new gas fee fields.
At the moment, we still use gas prices in the following manner:
I recommend reviewing each commit individually and sequentially.
Specific updates (required)
See commit history.
How did you test each of these updates (required)
Updated unit tests.
TODO: Manual testing.eth_maxPriorityFeePerGas
RPC callDoes this pull request close any open issues?
Fixes #1147
Fixes #1973
Fixes #1974
Fixes #1975
Checklist:
make
runs successfully./test.sh
pass