Block: 52

Timestamp: 02:10:05

AuditProfile

Security blog

Cross-chain gas fees for Optimism bridging

Did you know that the Optimism chain has its own gas payment system for cross-chain messages?

There was a problem in the Olas protocol that could lead to double payment for its users.

In particular, from the code below, it seems that you need to pay for the cross-chain gas by sending ETH to the Optimism CrossDomainMessenger. This can be seen in how a cost amount of ETH is passed to the CrossDomainMessenger contract.

    function _sendMessage(
        address[] memory targets,
        uint256[] memory stakingIncentives,
        bytes memory bridgePayload,
        uint256 transferAmount
    ) internal override returns (uint256 sequence) {
        // Check for the bridge payload length
        if (bridgePayload.length != BRIDGE_PAYLOAD_LENGTH) {
            revert IncorrectDataLength(BRIDGE_PAYLOAD_LENGTH, bridgePayload.length);
        }

        // Check for the transferAmount > 0
        if (transferAmount > 0) {
            // Deposit OLAS
            // Approve tokens for the predicate bridge contract
            // Source: https://github.com/maticnetwork/pos-portal/blob/5fbd35ba9cdc8a07bf32d81d6d1f4ce745feabd6/flat/RootChainManager.sol#L2218
            IToken(olas).approve(l1TokenRelayer, transferAmount);

            // Transfer OLAS to L2 staking dispenser contract across the bridge
            IBridge(l1TokenRelayer).depositERC20To(olas, olasL2, l2TargetDispenser, transferAmount,
                uint32(TOKEN_GAS_LIMIT), "");
        }

        // Decode cost related data
        (uint256 cost, uint256 gasLimitMessage) = abi.decode(bridgePayload, (uint256, uint256));
        // Check for zero values
        if (cost == 0 || gasLimitMessage == 0) {
            revert ZeroValue();
        }

        // Check for the max message gas limit
        if (gasLimitMessage > MESSAGE_GAS_LIMIT) {
            revert Overflow(gasLimitMessage, MESSAGE_GAS_LIMIT);
        }

        // Check that provided msg.value is enough to cover the cost
        if (cost > msg.value) {
            revert LowerThan(msg.value, cost);
        }

        // Assemble data payload
        bytes memory data = abi.encodeWithSelector(RECEIVE_MESSAGE, abi.encode(targets, stakingIncentives));

        // Send message to L2
        // Reference: https://docs.optimism.io/builders/app-developers/bridging/messaging#for-l1-to-l2-transactions-1
        IBridge(l1MessageRelayer).sendMessage{value: cost}(l2TargetDispenser, data, uint32(gasLimitMessage));

        // Since there is no returned message sequence, use the staking batch nonce
        sequence = stakingBatchNonce;
    }

This is incorrect, that is because instead, in Optimism, the cross-chain gas is paid by:

1. For L1 -> L2 transactions: the L1 gas is burnt in the Optimism bridge in Optimism's ResourceMetering to pay for the cross-chain gas in L2

2. For L2 -> L1 transactions: the user pays for the L1 gas by executing the functions proveWithdrawalTransaction and finalizeWithdrawalTransaction in OptimismPortal on the L1 to complete the cross-chain transactions

So in reality, there is no need to pass ETH to the CrossDomainMessenger to pay for the cross-chain gas.

Using the contracts as intended will cause unnecessary ETH to be sent and stuck in the OptimismDepositProcessorL1 and OptimismTargetDispenserL2, that is being misunderstood as the ETH being paid for the cross-chain gas.

Be carefull when using cross-chain messages.

Read the full report here:

Link: https://code4rena.com/reports/2024-05-olas#m-17-users-will-lose-all-eth-sent-as-cost-parameter-in-transactions-to-and-from-optimism

#optimism

#crosschain

#bridge

Connent with me:

Регистрация прошла успешно! Спасибо за внимание!

loader