Late Walnut Wombat
High
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.
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.
None
- The native token is pausable.
- The native token is paused.
- An attacker calls
finalizeWithdrawalTransaction()
with a withdrawal that uses the native token through the Cross Domain Messenger. - The
safeTransferFrom()
call reverts, and the withdrawal is lost.
Withdrawals can be bricked when the native token is paused, losing user funds.
N/A
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.