-
Notifications
You must be signed in to change notification settings - Fork 452
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
fix: arbitrum network cost estimate #464
Conversation
return { | ||
perL2TxFee: gasData[0], | ||
perL1CalldataFee: gasData[1], | ||
perL1CalldataFee: perL1CalldataByte.div(16), |
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 great catch! I took a look at OVM_GasPriceOracle, where we borrow the code to calculate L1 gas. Indeed the result has the factor of 16 multiplied.
You might notice that for Arbitrum gas estimate, smart-order-router should use the arbitrum L1 gas estimate, instead of optimism one. As a follow-up, Uniswap internally probably will need to use the gasEstimateL1Component, assuming this is pretty accurate estimate of the calldata costs that Arbitrum Sequencer posts to L1?
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.
that’s correct for a more accurate estimate but would require an extra rpc call, also note that it return in terms of L2 gas which should be multiplied with L2 gas price (instead of L1)
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 thought gasEstimateL1Component returns both L1 gas estimate, as well as L2 base fee?
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.
gasEstimateL1Component returns 3 things
- gasEstimateForL1: an estimate of the amount of gas needed for the l1 component of this tx
- l2BaseFee: the l2 base fee
- l1BaseFeeEstimate: ArbOS's l1 estimate of the l1 base fee
gasEstimateForL1 is how much L2 gas
we charge for the L1 calldata cost
so if you want to calculate feeForL1, that would be gasEstimateForL1 * l2BaseFee
if ([ChainId.ARBITRUM_ONE, ChainId.ARBITRUM_GOERLI].includes(chainId)) { | ||
l2toL1FeeInWei = calculateArbitrumToL1FeeFromCalldata( | ||
route.methodParameters!.calldata, | ||
l2GasData as ArbitrumGasData | ||
)[1]; | ||
} else if ( |
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 actually think there's no double counting of L1 fees here, although the reason is a bit complex.
You probably noticed two codepaths that invokes calculateGasUsed
:
Those two codepaths are actually mutually exclusive to each other, due to the check in FallbackTenderlySimulator.simulateTransaction. Then inside EthEstimateGasSimulator.ethEstimateGas, from Arbitrum doc, I see the node returns the entire transaction fee at the specified L2 gas price. I assume it does not include the L1 calldata posting costs. For Tenderly simulation, I'm a bit less certain if they include the L1 fee as well.
Taking a step back, the change inside calculateGasUsed
will likely have no effect, if your experience is such that you notice the abnormally high gas cost using the Uniswap web interface, not mobile, because only mobile requests a best swap quote with simulation, not web interface. The reason is a bit complex.
calculateGasUsed
is only going to be invoked when upstream requests smart order router to get a best swap route with forked net simulation. The code is inside AlphaRouter. Majority of the Arbitrum abnormal high gas cost report I see is the web app screenshot, so it means the Arbitrum gas cost is not recomputed inside gas-factory-helpers, but only once inside V3HeuristicGasModelFactory
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 assume it does not include the L1 calldata posting costs.
This is incorrect, unlike Optimism, Arbitrum charge L2 gas
for L1 calldata posting costs
. For example, a normal transfer cost 21000 L2 gas to execute and say 100000 L2 gas for L1 calldata, Arbitrum require the user to sign a transaction with at least 121000 gas to be included onchain. The RPC method "eth_estimateGas" on an Arbitrum node return the TOTAL L2 gas required, that is L2 execution gas + L1 calldata gas
which for the above example would be 121000. As far as I know, tenderly also have the same behavior (that the gas estimate include L1 calldata gas)
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.
In fact I don't really see why we need calculateArbitrumToL1FeeFromCalldata
anywhere except for display purpose (show user L1 and L2 fee split) because the gas estimation would have returned the total already. Unless you are hardcoding the execution gas somewhere and want to have a more accurate L1 fee added to it.
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 incorrect, unlike Optimism, Arbitrum charge L2 gas for L1 calldata posting costs.
This is good to know. This assumption is embedded in sor gas model. Let me think through how to fix this assumption.
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 am running the cli quoter, estimatedGasUsed
seems to be wrong but I cannot figure out where it come from. If it uses eth_estimateGas it should return a much higher number.
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.
Ah it does not do eth_estimateGas, it was semi-hardcoded in the gas model
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 I am not familiar with the SOR enough to properly fix this quickly, but would be nice if we can ship the div(16) fix first to make the number at least much more reasonable. Thanks for the quick response btw.
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.
Agree, there's a bit of urgency. Let's ship this tomorrow during EST/PST business hours. I can help create a replicated PR within
https://github.com/Uniswap/smart-order-router, so all the tests should pass.
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.
Cool, feel free to cherrypick my commit
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.
#466 replicated here
This is great stuff, thanks for the PR! Anecdotally, I saw a report, where the smart-order-router computed gas cost is about 130k, meanwhile the actual gas cost should be more than 3m. This means a factor of more than 24. Meanwhile this PR should address some discrepancies, I can see there's more to address. Most likely the fix will concentrate in V3HeuristicGasModelFactory. One thing on top of my mind is that smart order router does not compress the calldata for any L2 chains, but the L1 format calldata. That obviously will also inflate the gas cost by the compression ratio. |
Also would be great if you can add some unit test coverages. |
It can be more than 16x because Arbitrum charge L1 calldata gas after compression, the naive 16 gas a bytes formula does not account for that. Using
Tbh I don't know how to properly test this repo since some unit tests are failing before my changes |
We've hardened the repo by adding some test suites in the github CI. In order for all the tests to run successfully from your forked repo, you will need to create new repository secrets, for the following secrets used in https://github.com/Uniswap/smart-order-router/blob/main/.github/workflows/tests.yml:
However, I can see a bit urgency in fix the Arbitrum gas estimate, so can help by replicating this PR as a https://github.com/Uniswap/smart-order-router repo, since the repo contains all those secrets already. |
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.
Thank you for the PR!
Shall we also add some unit tests before we go through with this?
@@ -250,12 +250,8 @@ export async function calculateGasUsed( | |||
const gasPriceWei = route.gasPriceWei; | |||
// calculate L2 to L1 security fee if relevant | |||
let l2toL1FeeInWei = BigNumber.from(0); | |||
if ([ChainId.ARBITRUM_ONE, ChainId.ARBITRUM_GOERLI].includes(chainId)) { | |||
l2toL1FeeInWei = calculateArbitrumToL1FeeFromCalldata( |
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.
can we also delete the function definition since its not used anywhere else?
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 exported so idk
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.
Instead of deleting calculateArbitrumToL1FeeFromCalldata
, I think we will want to refactor the same code inside V3HeuristicGasModelFactory.
pushed some unit test |
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
This PR fixes 2 bugs that causes inflated network cost (gas fee) estimate for Arbitrum.
What is the current behavior? (You can also link to an open issue here)
Smart Order Router is inflating the Arbitrum fee due to double counting L1 fee and a incorrect unit conversion
What is the new behavior (if this is a feature change)?
Other information:
https://docs.arbitrum.io/devs-how-tos/how-to-estimate-gas
https://docs.arbitrum.io/arbos/gas
https://medium.com/offchainlabs/understanding-arbitrum-2-dimensional-fees-fd1d582596c9