refactor(contracts/core): improved variable packing to reduce gas costs #2291
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.
I noticed that the values used in FeeOracleV2 could be better optimized. To start, this was the original storage layout:
Four slots is a bit excessive, especially since the values we're working with are quite small. After some thoughtful resizing, this is what I reduced it to:
By reducing it from 4 slots to 2, much fewer SLOADs and SSTOREs are required for both all reads and writes.
I pushed this optimization even further by restructuring IFeeOracleV2.FeeParams. Here's the original structure, followed by the optimized one:
By reducing the sizing of the uint256 variables to uint64, all four variables fit within a single storage slot. This reduces R/W operations to
_feeParams
substantially from 5 slots to 2. Beforehand, you'd have one R/W to get the location, and then four R/W for each parameter. Because slots are 256 bits in size, the revised version in its entirety only requires one for the location and one for the R/W operation.To justify these changes, I looked at the numbers and figured out how high they could reasonably go. The
baseGasLimit
was originally a uint64, however, I do not think ethereum's base gas limit will ever approach 18_446_744_073_709_551_615, we have a long way from ~30-45M before we get there. A uint32 allows for 4_294_967_295, which is already well above existing capacity. With this reduced to 32 bits, and an address taking up 160 bits, we had 64 bits remaining. Conveniently, this is enough for theprotocolFee
, as I do not expect Omni to ever charge higher than 18.4 ETH for a single transaction.As for the gas prices themselves, and the native rate, none of these values will ever exceed uint64. If the gas price ever reached 18.4 ETH, we'd have worse problems on our hands to deal with, and I doubt anyone will be paying those gas prices for a crosschain transaction.
The protocolFee and baseGasLimit are the only parameters that really need any consideration. If we do not expect to ever raise this above 50k, we should use a uint16 (65_535) to store it, allowing us to increase the protocolFee to a uint80. The reasoning for this is because it would better support L2's with alternative gas tokens by having a protocol fee ceiling of ~1.2M tokens, as opposed to ~18.4 tokens. For chains that use ETH, the current configuration is fine, but there may come a time where we support L2s that don't.
After all of these changes, I was able to reduce the costs of the following function calls (via test gas metering):
issue: #1951