-
Notifications
You must be signed in to change notification settings - Fork 54
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
Charge l1 data availability fee #86
Conversation
LCOV of commit
|
Go solidity wrappers are out-of-date, regenerate them via the |
|
||
// little endian | ||
function _unpackUint224Into112(uint224 value) internal pure returns (uint112, uint112) { | ||
return (uint112(value & UINT_112__MASK), uint112((value >> 112) & UINT_112__MASK)); |
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.
Do you need the mask or is downcasting enough 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.
good point, downcasting truncates higher order bits, that'll just work
LCOV of commit
|
Go solidity wrappers are out-of-date, regenerate them via the |
LCOV of commit
|
Go solidity wrappers are out-of-date, regenerate them via the |
LCOV of commit
|
Go solidity wrappers are out-of-date, regenerate them via the |
LCOV of commit
|
Go solidity wrappers are out-of-date, regenerate them via the |
LCOV of commit
|
uint16 maxTokensLength; // | Maximum number of ERC20 token transfers that can be included per message | ||
uint32 destGasOverhead; // | Extra gas charged on top of the gasLimit | ||
uint16 destGasPerPayloadByte; // | Destination chain gas charged per byte of `data` payload | ||
uint32 destCalldataOverhead; // | Extra L1 calldata gas on top of the message used in security 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.
let's add more info here, because L1 doesn't make sense for any L1. Also, i've never heard the term "security 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.
I've heard of security fee, but as far as nomenclature goes I think DA (data availability) is the most general purpose/generic and we should use it everywhere. L2's happen to store the data as calldata, but need not, its really just about paying a fee to ensure the data is made available. Should be able to leave out the notion of L1/L2's all together as well
uint32 destGasOverhead; // | Extra gas charged on top of the gasLimit | ||
uint16 destGasPerPayloadByte; // | Destination chain gas charged per byte of `data` payload | ||
uint32 destCalldataOverhead; // | Extra L1 calldata gas on top of the message used in security fee | ||
uint16 destGasPerCalldataByte; // ---┘ Number of L1 calldata gas to charge per byte of message |
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.
nit: amount of instead of number of
return evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{}, err | ||
} | ||
return evm_2_evm_onramp.EVM2EVMOnRampDynamicConfig{ | ||
Router: legacyDynamicConfig.Router, |
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.
You're missing some values that are in 1.1
@@ -39,7 +39,7 @@ type CCIPE2ELoad struct { | |||
CallTimeOut time.Duration // max time to wait for various on-chain events | |||
reports *testreporters.CCIPLaneStats | |||
msg router.ClientEVM2AnyMessage | |||
MaxDataSize uint32 | |||
MaxDataSize *big.Int |
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.
Is this big int because go doesn' have uint24?
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 so.
uint16 gives us 64KB, that's not enough, Ethereum can do >120KB.
uint32 gives us 4GB, that's seems a bit too big.
so went with uint24, and it become big.int in generated code
@@ -34,15 +34,15 @@ contract PriceRegistry is IPriceRegistry, OwnerIsCreator { | |||
/// Very Expensive: 1 unit of gas costs 1 USD -> 1e18 | |||
/// Expensive: 1 unit of gas costs 0.1 USD -> 1e17 | |||
/// Cheap: 1 unit of gas costs 0.000001 USD -> 1e12 | |||
mapping(uint64 destChainSelector => Internal.TimestampedUint192Value price) | |||
mapping(uint64 destChainSelector => Internal.TimestampedPackedUint224 price) |
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.
comments need updating
|
||
/// @notice Get the `tokenPrice` for an array of tokens. | ||
/// @param tokens The tokens to get prices for. | ||
/// @return tokenPrices The tokenPrices for the given tokens. | ||
function getTokenPrices(address[] calldata tokens) external view returns (Internal.TimestampedUint192Value[] memory); | ||
function getTokenPrices(address[] calldata tokens) external view returns (Internal.TimestampedPackedUint224[] memory); | ||
|
||
/// @notice Get the `gasPrice` for a given destination chain ID. | ||
/// @param destChainSelector The destination chain to get the price for. | ||
/// @return gasPrice The gasPrice for the given destination chain ID. | ||
function getDestinationChainGasPrice( |
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.
probably update the comment here? it now returns a multi-dimensional gas price
} | ||
|
||
/// @dev Gas price is stored in 112-bit unsigned int. uint224 can pack 2 prices. |
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 abstract this from price reg clients all together? Since the price reg API is breaking anyways, something like getValidatedExecutionPrices(destinationSelector)->(gasPrice, daPrice)
where the 2 return values are already parsed for the client. Also that way I think we don't even need the multiplier check (if daPrice == 0, then DA is free/covered by gasPrice)
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.
spoke offline - I'm cool with opaque gas price and ramps interpreting
@@ -13,7 +13,7 @@ import {EnumerableSet} from "../vendor/openzeppelin-solidity/v4.8.0/contracts/ut | |||
/// and the price of a token in USD allowing the owner or priceUpdater to update this value. | |||
contract PriceRegistry is IPriceRegistry, OwnerIsCreator { | |||
using EnumerableSet for EnumerableSet.AddressSet; | |||
using USDPriceWith18Decimals for uint192; |
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.
great opportunity to add a type and version to the price reg
) external view override returns (uint192 tokenPrice, uint192 gasPriceValue) { | ||
Internal.TimestampedUint192Value memory gasPrice = s_usdPerUnitGasByDestChainSelector[destChainSelector]; | ||
) external view override returns (uint224 tokenPrice, uint224 gasPriceValue) { | ||
Internal.TimestampedPackedUint224 memory gasPrice = s_usdPerUnitGasByDestChainSelector[destChainSelector]; |
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.
thoughts on an internal _getValidatedExecutionPrices analogous to the _getValidatedTokenPrice? Yields some nice symmetry validatedXX means timestamp checked for both gas and tokens
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.
will include in a follow up PR that includes _getValidatedGasPrices
and getValidatedExecutionPrices
uint16 maxTokensLength; // | Maximum number of ERC20 token transfers that can be included per message | ||
uint32 destGasOverhead; // | Extra gas charged on top of the gasLimit | ||
uint16 destGasPerPayloadByte; // | Destination chain gas charged per byte of `data` payload | ||
uint32 destCalldataOverhead; // | Extra L1 calldata gas on top of the message used in security 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.
I've heard of security fee, but as far as nomenclature goes I think DA (data availability) is the most general purpose/generic and we should use it everywhere. L2's happen to store the data as calldata, but need not, its really just about paying a fee to ensure the data is made available. Should be able to leave out the notion of L1/L2's all together as well
@@ -9,19 +9,24 @@ library Internal { | |||
struct PriceUpdates { | |||
TokenPriceUpdate[] tokenPriceUpdates; | |||
uint64 destChainSelector; // --┐ Destination chain selector | |||
uint192 usdPerUnitGas; // -----┘ 1e18 USD per smallest unit (e.g. wei) of destination chain gas | |||
uint224 usdPerUnitGas; // -----┘ 1e18 USD per smallest unit (e.g. wei) of destination chain 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.
while we're changing this interface, we should open the door for batched gas price updates i.e. have a GasPriceUpdate[] where GasPriceUpdate{DestChainSelector, GasPrice}. Can be a follow up if painful to add now
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.
As long as it is in 1.2 and doesn't bleed into 1.3 I don't mind doing it in follow up
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'll post it in a followup PR, this is PR is already quite big.
/// Each variable array takes 1 more slot to store its length. | ||
/// Hence, when abiEncoded, excluding the dynamic contents of variable arrays, | ||
/// EVM2EVMMessage takes up a fixed number of 14 lots, 32 bytes each. | ||
uint256 public constant MESSAGE_FIXED_BYTES = 32 * 14; |
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.
hmm I'm ok with it here as its a tightly coupled property of EVM2EVMMessage
uint256 numberOfTokens, | ||
uint32 tokenTransferBytesOverhead | ||
) internal view returns (uint256 calldataCostUSD) { | ||
uint256 calldataLength = Internal.MESSAGE_FIXED_BYTES + |
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.
would use units suffixes everywhere i.e. calldataLengthBytes, s_dynamicConfig.destCalldataOverheadGas etc
LCOV of commit
|
@@ -29,20 +30,27 @@ contract PriceRegistry is IPriceRegistry, OwnerIsCreator { | |||
event UsdPerUnitGasUpdated(uint64 indexed destChain, uint256 value, uint256 timestamp); | |||
event UsdPerTokenUpdated(address indexed token, uint256 value, uint256 timestamp); | |||
|
|||
/// @dev The price, in USD with 18 decimals, of 1 unit of gas for a given destination chain. | |||
// STATIC CONFIG |
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.
not sure we need this header for a single item
/// @notice Get the `gasPrice` for a given destination chain ID. | ||
/// @notice Get an encoded `gasPrice` for a given destination chain ID. | ||
/// The 224-bit result could encode multiple price components. For example, if Optimism is the destination chain, | ||
/// gas price can include L1 base fee in higher-order bits, and L2 gas price in lower-order bits. |
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.
nit: gas price can include
it's not can, it always will, right? If not explain when this is and isnt the case
uint16 destGasPerDataAvailabilityByte; // --┘ Amount of gas to charge per byte of data that needs availability | ||
uint16 destDataAvailabilityMultiplier; // --┐ Multiplier for data availability gas, multples of 1e-4, or 0.0001 | ||
address priceRegistry; // | Price registry address | ||
uint24 maxDataSize; // | Maximum payload data size, max 16MB |
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.
Let's make this uint32 as it fits and 24 is a strange datatype that becomes big.int offchain
LCOV of commit
|
LCOV of commit
|
LCOV of commit
|
LCOV of commit
|
LCOV of commit
|
* add uint224 price split and call data config fields * add calldata calculation * address early comments * add more comments to explain config values * completing onramp * fill in transfer fee tests * add tests for getMessageCalldataCostUSD * onchain tests complete * complete onchain portion * update offchain tests * update snapshot * fix loadgen test MaxDataSize * better variable names * update changelog * address comments * address comments * re-generate snapshot * update loadgen * reset submodule * update snapshot
Motivation
When sending to supported L2 rollups, we undercharge the fee as execution gas cost only represents tiny portion of the fee. Most of the cost at destination comes from L1 security fee.
Solution
Currently supported L2 rollups are optimistic rollups. Their L1 security fee can be estimated with equation