diff --git a/specs/protocol/bridges.md b/specs/protocol/bridges.md index 319b7b0a5..0a8f2284d 100644 --- a/specs/protocol/bridges.md +++ b/specs/protocol/bridges.md @@ -6,6 +6,7 @@ - [Overview](#overview) - [Token Depositing](#token-depositing) +- [ERC20 Unlocking](#erc20-unlocking) - [Upgradability](#upgradability) @@ -29,15 +30,19 @@ interface StandardBridge { event ERC20BridgeInitiated(address indexed localToken, address indexed remoteToken, address indexed from, address to, uint256 amount, bytes extraData); event ETHBridgeFinalized(address indexed from, address indexed to, uint256 amount, bytes extraData); event ETHBridgeInitiated(address indexed from, address indexed to, uint256 amount, bytes extraData); + event ERC20Unlocked(address indexed localToken, address indexed remotetoken, address indexed from, uint256 amount, bytes32 messageHash); function bridgeERC20(address _localToken, address _remoteToken, uint256 _amount, uint32 _minGasLimit, bytes memory _extraData) external; function bridgeERC20To(address _localToken, address _remoteToken, address _to, uint256 _amount, uint32 _minGasLimit, bytes memory _extraData) external; function bridgeETH(uint32 _minGasLimit, bytes memory _extraData) payable external; function bridgeETHTo(address _to, uint32 _minGasLimit, bytes memory _extraData) payable external; function deposits(address, address) view external returns (uint256); + function processedMessages(bytes32 _messageHashes) view external returns (uint256); + function unlockERC20s(address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes calldata _extraData, uint240 _nonce, uint32 _minGasLimit) external; function finalizeBridgeERC20(address _localToken, address _remoteToken, address _from, address _to, uint256 _amount, bytes memory _extraData) external; function finalizeBridgeETH(address _from, address _to, uint256 _amount, bytes memory _extraData) payable external; function messenger() view external returns (address); + function ROLLBACK_INBOX() view external returns (address); function OTHER_BRIDGE() view external returns (address); } ``` @@ -49,6 +54,13 @@ domain. An `OptimismMintableERC20` token contract must exist on the remote domain to be able to deposit tokens to that domain. One of these tokens can be deployed using the `OptimismMintableERC20Factory` contract. +## ERC20 Unlocking + +The `ERC20Unlocked` function is used to unlock tokens stuck due to failure in +relaying prior ERC20 bridging actions. A `messageHash` must exist in the `ROLLBACK_INBOX` +contract to certify the message hash corresponding to an ERC20 bridging action started +from the standard bridged failed on the other domain. + ## Upgradability Both the L1 and L2 standard bridges should be behind upgradable proxies. diff --git a/specs/protocol/messengers.md b/specs/protocol/messengers.md index 142a4e2dd..66f9433e0 100644 --- a/specs/protocol/messengers.md +++ b/specs/protocol/messengers.md @@ -6,6 +6,7 @@ - [Overview](#overview) - [Message Passing](#message-passing) +- [Rolling a Message Back](#rolling-a-message-back) - [Upgradability](#upgradability) - [Message Versioning](#message-versioning) - [Message Version 0](#message-version-0) @@ -38,6 +39,7 @@ interface CrossDomainMessenger { event RelayedMessage(bytes32 indexed msgHash); event SentMessage(address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); event SentMessageExtension1(address indexed sender, uint256 value); + event MessageSentToRollbackInbox(bytes32 indexed messageHash, address indexed target, address sender, bytes message, uint256 messageNonce, uint256 gasLimit); function MESSAGE_VERSION() external view returns (uint16); function MIN_GAS_CALLDATA_OVERHEAD() external view returns (uint64); @@ -45,8 +47,10 @@ interface CrossDomainMessenger { function MIN_GAS_DYNAMIC_OVERHEAD_DENOMINATOR() external view returns (uint64); function MIN_GAS_DYNAMIC_OVERHEAD_NUMERATOR() external view returns (uint64); function OTHER_MESSENGER() external view returns (address); + function ROLLBACK_INBOX() external view returns (address); + function ROLLBACK_INBOX_DELAY() external view returns (uint256); function baseGas(bytes memory _message, uint32 _minGasLimit) external pure returns (uint64); - function failedMessages(bytes32) external view returns (bool); + function failedMessages(bytes32) external view returns (uint256); function messageNonce() external view returns (uint256); function relayMessage( uint256 _nonce, @@ -57,7 +61,8 @@ interface CrossDomainMessenger { bytes memory _message ) external payable; function sendMessage(address _target, bytes memory _message, uint32 _minGasLimit) external payable; - function successfulMessages(bytes32) external view returns (bool); + function sendHashToRollbackInbox(bytes32 _versionedHash, bytes32 _minGasLimit) external; + function successfulMessages(bytes32) external view returns (uint256); function xDomainMessageSender() external view returns (address); } ``` @@ -79,6 +84,16 @@ then waits for the finalization window to pass, and then finalizes the withdrawa on the OptimismPortal, which calls `relayMessage` on the `L1CrossDomainMessenger` to finalize the withdrawal. +## Rolling a Message Back + +The `sendHashToRollbackInbox` function is used to send the hashes of failed +and unsuccesful messages to the `ROLLBACK_INBOX` contract on the other domain +after a given delay, ensuring the message corresponding to the rolled back hash +is never processed in the current domain. The main purpose of this function +is to inform the other domain of failed messages so contracts living in +this other domain can handle the message failure. This allows functionalities +such as unlocking stuck ERC20s. + ## Upgradability The L1 and L2 cross domain messengers should be deployed behind upgradable diff --git a/specs/protocol/rollback-inboxes.md b/specs/protocol/rollback-inboxes.md new file mode 100644 index 000000000..7c5118037 --- /dev/null +++ b/specs/protocol/rollback-inboxes.md @@ -0,0 +1,41 @@ +# Rollback Inboxes + + + +**Table of Contents** + +- [Overview](#overview) +- [Message Hash Reception](#message-hash-reception) +- [Upgradability](#upgradability) + + + +## Overview + +The rollback inbox contracts are responsible for storing the hashes of messages +that failed on the other domain. They are built with the purpose of allowing contracts +on the same domain to have the capability of handling what actions to perform when a +message failed on the other domain and was rolled-back to it's origin domain. + +The `L2RollbackInbox` is a predeploy contract located at TO BE DEFINED. + +```solidity +interface RollbackInbox { + event MessageHashReceived(bytes32 indexed messageHash, uint256 indexed timestamp); + + function receiveMessageHash(bytes32 _messageHash, address _sender) external; + function messenger() view external returns (address); + function otherMessenger() view external returns (address); + function messageHashes(bytes32 _messageHash) view external returns (uint256); +} +``` + +## Message Hash Reception + +The `receiveMessageHash` function is used to receive message hashes from the other domain. +It must ensure the caller is the `CrossDomainMessenger` from this domain, and that the +sender is the `CrossDomainMessenger` from the other domain. + +## Upgradability + +Both the L1 and L2 rollback inboxes should be behind upgradable proxies.