Gas usage of cross-chain messages is undercounted, causing discrepancy between L1 and L2 and impacting intrinsic gas calculation
Gas consumption of messages sent via CrossDomainMessenger (including both L1CrossDomainMessenger and L2CrossDomainMessenger) is calculated incorrectly: the gas usage of the "relayMessage" wrapper is not counted. As a result, the actual gas consumption of sending a message will be higher than expected. Users will pay less for gas on L1, and L2 blocks may be filled earlier than expected. This will also affect gas metering via ResourceMetering: metered gas will be lower than actual consumed gas, and the EIP-1559-like gas pricing mechanism won't reflect the actual demand for gas.\\nThe CrossDomainMessenger.sendMessage function is used to send cross-chain messages. Users are required to set the `_minGasLimit` argument, which is the expected amount of gas that the message will consume on the other chain. The function also computes the amount of gas required to pass the message to the other chain: this is done in the `baseGas` function, which computes the byte-wise cost of the message. `CrossDomainMessenger` also allows users to replay their messages on the destination chain if they failed: to allow this, the contract wraps user messages in `relayMessage` calls. This increases the size of messages, but the `baseGas` call above counts gas usage of only the original, not wrapped in the `relayMessage` call, message.\\nThis contradicts the intrinsic gas calculation in `op-geth`, which calculates gas of an entire message data:\\n```\\ndataLen := uint64(len(data))\\n// Bump the required gas by the amount of transactional data\\nif dataLen > 0 {\\n    // rest of code\\n}\\n```\\n\\nThus, there's a discrepancy between the contract and the node, which will result in the node consuming more gas than users paid for.\\nThis behaviour also disagrees with how the migration process works:\\nwhen migrating pre-Bedrock withdrawals, `data` is the entire messages, including the `relayMessage` calldata;\\nthe gas limit of migrated messages is computed on the entire `data`.\\nTaking into account the logic of paying cross-chain messages' gas consumption on L1, I think the implementation in the migration code is correct and the implementation in `CrossDomainMessenger` is wrong: users should pay for sending the entire cross-chain message, not just the calldata that will be execute on the recipient on the other chain.
When counting gas limit in the `CrossDomainMessenger.sendMessage` function, consider counting the entire message, including the `relayMessage` calldata wrapping. Consider a change like that:\\n```\\ndiff // Remove the line below\\n// Remove the line below\\ngit a/packages/contracts// Remove the line below\\nbedrock/contracts/universal/CrossDomainMessenger.sol b/packages/contracts// Remove the line below\\nbedrock/contracts/universal/CrossDomainMessenger.sol\\nindex f67021010..5239feefd 100644\\n// Remove the line below\\n// Remove the line below\\n// Remove the line below\\n a/packages/contracts// Remove the line below\\nbedrock/contracts/universal/CrossDomainMessenger.sol\\n// Add the line below\\n// Add the line below\\n// Add the line below\\n b/packages/contracts// Remove the line below\\nbedrock/contracts/universal/CrossDomainMessenger.sol\\n@@ // Remove the line below\\n253,19 // Add the line below\\n253,20 @@ abstract contract CrossDomainMessenger is\\n         // message is the amount of gas requested by the user PLUS the base gas value. We want to\\n         // guarantee the property that the call to the target contract will always have at least\\n         // the minimum gas limit specified by the user.\\n// Add the line below\\n        bytes memory wrappedMessage = abi.encodeWithSelector(\\n// Add the line below\\n            this.relayMessage.selector,\\n// Add the line below\\n            messageNonce(),\\n// Add the line below\\n            msg.sender,\\n// Add the line below\\n            _target,\\n// Add the line below\\n            msg.value,\\n// Add the line below\\n            _minGasLimit,\\n// Add the line below\\n            _message\\n// Add the line below\\n        );\\n         _sendMessage(\\n             OTHER_MESSENGER,\\n// Remove the line below\\n            baseGas(_message, _minGasLimit),\\n// Add the line below\\n            baseGas(wrappedMessage, _minGasLimit),\\n             msg.value,\\n// Remove the line below\\n            abi.encodeWithSelector(\\n// Remove the line below\\n                this.relayMessage.selector,\\n// Remove the line below\\n                messageNonce(),\\n// Remove the line below\\n                msg.sender,\\n// Remove the line below\\n                _target,\\n// Remove the line below\\n                msg.value,\\n// Remove the line below\\n                _minGasLimit,\\n// Remove the line below\\n                _message\\n// Remove the line below\\n            )\\n// Add the line below\\n            wrappedMessage\\n         );\\n\\n         emit SentMessage(_target, msg.sender, _message, messageNonce(), _minGasLimit);\\n```\\n
Since the `CrossDomainMessenger` contract is recommended to be used as the main cross-chain messaging contract and since it's used by both L1 and L2 bridges (when bridging ETH or ERC20 tokens), the undercounted gas will have a broad impact on the system. It'll create a discrepancy in gas usage and payment on L1 and L2: on L1, users will pay for less gas than actually will be consumed by cross-chain messages.\\nAlso, since messages sent from L1 to L2 (via OptimismPortal.depositTransaction) are priced using an EIP-1559-like mechanism (via ResourceMetering._metered), the mechanism will fail to detect the actual demand for gas and will generally set lower gas prices, while actual gas consumption will be higher.\\nThe following bytes are excluded from gas usage counting:\\nthe 4 bytes of the `relayMessage` selector;\\nthe 32 bytes of the message nonce;\\nthe address of the sender (20 bytes);\\nthe address of the recipient (20 bytes);\\nthe amount of ETH sent with the message (32 bytes);\\nthe minimal gas limit of the nested message (32 bytes).\\nThus, every cross-chain message sent via the bridge or the messenger will contain 140 bytes that won't be paid by users. The bytes will however be processed by the node and accounted in the gas consumption.
```\\ndataLen := uint64(len(data))\\n// Bump the required gas by the amount of transactional data\\nif dataLen > 0 {\\n    // rest of code\\n}\\n```\\n