eth: Ensure maxFeePerGas in all transactions never exceed -masGasPrice defined by the user#2111
Conversation
|
Reaction based on a quick skim: I think this draft PR is going in the right direction. My understanding of the transaction flow after these changes:
|
|
Thanks for the quick feedback @yondonfu . I understand it exactly the same as you do. So, I'll perform the testing and if everything works fine, I'll update this draft PR. |
maxFeePerGas in all transactions never exceed -masGasPrice defined by the user
8b9a5bd to
1566bef
Compare
1566bef to
fa6d530
Compare
| func (tm *TransactionManager) newAdjustedTx(tx *types.Transaction) *types.Transaction { | ||
| baseTx := newDynamicFeeTx(tx) | ||
| if tm.gpm.MaxGasPrice() != nil { | ||
| baseTx.GasFeeCap = tm.gpm.MaxGasPrice() |
There was a problem hiding this comment.
I forget if we talked about this, but is there a reason for always setting this to the max gas price as opposed to only setting it to the max gas price if the max gas price exceeds the current value?
There was a problem hiding this comment.
Both approaches will work. However, IMO the always setting max gas price is slightly better because:
- It's simpler for the user what happens.
- Since the replacement transaction needs to be at least 10% higher, we may end up never reaching exactly
maxGasPrice.
What do you think?
There was a problem hiding this comment.
Sounds reasonable. But, this also means that a replacement tx would never be sent if the user sets a maxGasPrice because GasFeeCap is always set to maxGasPrice meaning that we are never able to increase GasFeeCap for a replacement tx. And the only scenario where a replacement tx could be sent is if the user does not set a maxGasPrice. I think this is okay, but just want to make sure we're on the same page about the expected behavior.
There was a problem hiding this comment.
You're right. Actually, there is one more scenario for the replacement if the user manually increases maxGasPrice while the transaction is pending. Then the replacement can also happen.
| func (tm *TransactionManager) newAdjustedTx(tx *types.Transaction) *types.Transaction { | ||
| baseTx := newDynamicFeeTx(tx) | ||
| if tm.gpm.MaxGasPrice() != nil { | ||
| baseTx.GasFeeCap = tm.gpm.MaxGasPrice() |
There was a problem hiding this comment.
Both approaches will work. However, IMO the always setting max gas price is slightly better because:
- It's simpler for the user what happens.
- Since the replacement transaction needs to be at least 10% higher, we may end up never reaching exactly
maxGasPrice.
What do you think?
yondonfu
left a comment
There was a problem hiding this comment.
As of this PR, I think the -maxGasPrice flag is basically equivalent to maxFeePerGas since its value is always set as GasTipCap for transactions. The gas price check in backend.SuggestGasPrice() can be interpreted as:
- The gas price cached by the GasPriceMonitor could be interpreted as "a suggested maxFeePerGas" i.e. the current base fee + a suggested priority fee
- We compare the user provided
maxFeePerGas(via-maxGasPrice) against this suggested maxFeePerGas
If this is the case, I think we should make some small updates to docs in a few places:
| func (tm *TransactionManager) newAdjustedTx(tx *types.Transaction) *types.Transaction { | ||
| baseTx := newDynamicFeeTx(tx) | ||
| if tm.gpm.MaxGasPrice() != nil { | ||
| baseTx.GasFeeCap = tm.gpm.MaxGasPrice() |
There was a problem hiding this comment.
Sounds reasonable. But, this also means that a replacement tx would never be sent if the user sets a maxGasPrice because GasFeeCap is always set to maxGasPrice meaning that we are never able to increase GasFeeCap for a replacement tx. And the only scenario where a replacement tx could be sent is if the user does not set a maxGasPrice. I think this is okay, but just want to make sure we're on the same page about the expected behavior.
That is correct.
That is not correct. "suggested gas price" from GasPriceMonitor is not interpreted as You made me think we should make some cleanup, but I believe it should be a separate PR, not to mix to many things here.
|
That method is actually called in backend.SuggestGasTipCap() right now. And
Yeah I was a bit off there. So, the I was just thinking about how to describe what "gas price" means. I think the way to describe this is:
How does that sound to you?
Okay yeah so if |
This is correct with one comment. The function
Yes, I think it's right.
I'm not sure what's the meaning of |
|
@yondonfu I updated the doc again after our conversation. PTAL. |
What does this pull request do? Explain your changes. (required)
Limit the maxFeePerGas in the eth transaction to the
-maxGasPricevalue defined by the user. Before it was possible that the user paid more than they specified as-maxGasPrice.Additionally, I removed the
calcGasPrice()logic fromtransactionManager.go, because I believe it was incorrect. It assumed thatmaxFeePerGaswill be calculated in the go-ethereum code, but I think we override it in our code here.Specific updates (required)
GasFeeCapbefore sending the transactioncalcGasPricefunction inreplace()How did you test each of these updates (required)
I did test the change both locally and using Rinkeby.
Local testing
maxFeePerGas:1000000014(2 * base fee + priority fee)max gas price(via livepeer CLI) to1000000013maxFeePerGasis set to1000000013max gas priceto a value which is too low, e.g.5Rinkeby testing
rinkebyhttps://rinkeby.etherscan.io/tx/0x382e9a5b7635862fd8a5ac64ddf7e1b1d6111c4b67cb1318c13d7fede3fa31f0
1000000055and run the transaction againhttps://rinkeby.etherscan.io/tx/0xc6cfc72bb7aef2e4f026332ebcf9eec00ba10b4a0a94290d7c59a3bc0d1f8e9f
Does this pull request close any open issues?
fix #2084
Checklist:
makeruns successfully./test.shpass