Skip to content

Latest commit

 

History

History
65 lines (43 loc) · 3.05 KB

File metadata and controls

65 lines (43 loc) · 3.05 KB

Late Walnut Wombat

High

Pausable native token can lead to lost withdrawals

Summary

Any call from the Optimism Portal to the Cross Domain Messenger that reverts without saving a value in the failedMessages mapping will not be replayable, and thus will be lost forever. In the event that a pausable token is chosen for the native token, the transferFrom() call could cause such a revert.

Root Cause

All withdrawals finalized through the Optimism Portal can only be played once. Regardless of the outcome, the message will not be replayable. The purpose of the Cross Domain Messenger is to catch these failed transactions so they can be replayed. Thus, it is critically important that any call through the Cross Domain Messenger that reverts is saved in the failedMessages mapping for future replayability.

In the event that the L2 withdrawal had a msg.value, we will need to send the native token to the user on L1.

This happens in the Cross Domain Messenger's relayMessage() function here:

if (_isOtherMessenger()) {
    // These properties should always hold when the message is first submitted (as
    // opposed to being replayed).
    assert(!failedMessages[versionedHash]);
    if (_value > 0) {
        IERC20(_nativeTokenAddress).safeTransferFrom(address(portal), address(this), _value);
    }
    ...
}

Any token can be chosen for a network's NativeToken, provided it follows the properties defined in the README:

- Has 18 decimals. The L2 native token has 18 decimals, so the corresponding L1 token must have exactly 18 decimals to ensure no loss of precision when depositing or withdrawing.
- Must not have fees or hooks on transfer.
- Must not have out-of-band methods for modifying balance or allowance. For example, no tokes that have rebasing logic or double entry points can be an L2 native token.
- Any other requirements set by a standard bridge that is not mentioned here.

This does not preclude a token that contains a pause() function (specifically, a pause() function that reverts on transfers and not approvals).

In this case, the safeTransferFrom() call above will revert, nothing will be saved in the Cross Domain Messenger, and the withdrawal will be permanently lost.

Internal Preconditions

None

External Preconditions

  1. The native token is pausable.

Attack Path

  1. The native token is paused.
  2. An attacker calls finalizeWithdrawalTransaction() with a withdrawal that uses the native token through the Cross Domain Messenger.
  3. The safeTransferFrom() call reverts, and the withdrawal is lost.

Impact

Withdrawals can be bricked when the native token is paused, losing user funds.

PoC

N/A

Mitigation

Low level calls should be used for all external calls from the relayMessage() function, and in the event that the call fails for any reason, we should set the failedMessages mapping and return early, instead of reverting.