-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
interop: interoperable ether transfers
- Loading branch information
1 parent
cb74ef8
commit e180fb3
Showing
1 changed file
with
154 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
# Purpose | ||
|
||
This document exists to align on a design for simplifying the process for sending `ETH` between two interoperable chains. | ||
|
||
# Summary | ||
|
||
New functionality is introduced to the `SuperchainWETH` contract that enables it to accept `ETH` and facilitate cross-chain `ETH` transfers to a specified recipient. | ||
|
||
# Problem Statement + Context | ||
|
||
Currently, L2-to-L2 `ETH` transfers between two interoperable chains require four separate transactions: | ||
|
||
1. Wrap `ETH` to `SuperchainWETH`. | ||
2. Call `SuperchainTokenBridge#SendERC20` to burn `SuperchainWETH` on source chain and relay a message to the destination chain that mints `SuperchainWETH` to the recipient. | ||
3. Execute the message on the destination chain, triggering `SuperchainTokenBridge#RelayERC20` to mint `SuperchainWETH` to the recipient. | ||
4. Unwrap the received `SuperchainWETH` to `ETH`. | ||
|
||
The goal is to reduce the transaction count from four to two, enabling users to send `ETH` to a destination chain directly: | ||
|
||
1. Burn `ETH` on source chain and relay a message to destination chain that mints `ETH` to recipient on destination chain. | ||
2. Execute the relayed message on the destination chain that mints `ETH` to the recipient. | ||
|
||
# Proposed Solution | ||
|
||
Add two new functions to the `SuperchainWETH` contract: `SendETH` and `RelayETH`. | ||
|
||
### `SendETH` | ||
|
||
The `SendETH` function combines the first two transactions as follows: | ||
|
||
1. Burns `ETH` within the `ETHLiquidity` contract equivalent to the `ETH` sent. | ||
2. Sends a message to the destination chain encoding a call to `RelayETH`. | ||
|
||
### `RelayETH` | ||
|
||
The `RelayETH` function combines the last two transactions as follows: | ||
|
||
1. Mints the specified `_amount` of `ETH` from the `ETHLiquidity` contract. | ||
2. Sends the minted `ETH` to the specified recipient. | ||
|
||
### Contract changes | ||
|
||
```solidity | ||
/// @notice Sends ETH to some target address on another chain. | ||
/// @param _to Address to send tokens to. | ||
/// @param _chainId Chain ID of the destination chain. | ||
function sendETH(address _to, uint256 _chainId) public payable { | ||
if (IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) { | ||
revert NotCustomGasToken(); | ||
} | ||
// Burn to ETHLiquidity contract. | ||
IETHLiquidity(Predeploys.ETH_LIQUIDITY).burn{ value: msg.value }(); | ||
// Send message to other chain. | ||
IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER).sendMessage({ | ||
_destination: _chainId, | ||
_target: address(this), | ||
_message: abi.encodeCall(this.relayETH, (msg.sender, _to, msg.value)) | ||
}); | ||
// Emit event. | ||
emit SendETH(msg.sender, _to, msg.value, _chainId); | ||
} | ||
/// @notice Relays ETH received from another chain. | ||
/// @param _from Address of the msg.sender of sendETH on the source chain. | ||
/// @param _to Address to relay tokens to. | ||
/// @param _amount Amount of tokens to relay. | ||
function relayETH(address _from, address _to, uint256 _amount) external { | ||
// Receive message from other chain. | ||
IL2ToL2CrossDomainMessenger messenger = IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); | ||
if (msg.sender != address(messenger)) revert CallerNotL2ToL2CrossDomainMessenger(); | ||
if (messenger.crossDomainMessageSender() != address(this)) revert InvalidCrossDomainSender(); | ||
if (IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) { | ||
revert NotCustomGasToken(); | ||
} | ||
// Mint from ETHLiquidity contract. | ||
IETHLiquidity(Predeploys.ETH_LIQUIDITY).mint(_amount); | ||
// Get source chain ID. | ||
uint256 source = messenger.crossDomainMessageSource(); | ||
new SafeSend{ value: _amount }(payable(_to)); | ||
// Emit event. | ||
emit RelayETH(_from, _to, _amount, source); | ||
} | ||
``` | ||
|
||
Since the `SuperchainWETH` contract already has permissions to mint and burn `ETH` with the `ETHLiquidity` contract no changes to the `ETHLiquidity` contract are necessary. | ||
|
||
A potential drawback of this approach is that it creates a second entrypoint outside of the `SuperchainTokenBridge` for performing asset transfers. However, there will already be alternative etnrypoints for asset transfers for any `SuperchainERC20` tokens that implement their own custom bridging. | ||
|
||
# Considerations | ||
|
||
## Custom Gas Token Chains | ||
|
||
To simplify the solution, custom gas token chains will not be supported and must follow the original four-transaction flow, which includes wrapping and unwrapping `SuperchainWETH`. | ||
|
||
# Open Questions | ||
|
||
- **Naming**: Should `SuperchainWETH` be renamed now that it also handles native `ETH` transfers? | ||
- **Long-term improvements**: Could this functionality eventually extend to custom gas token chains? | ||
- **Rollbacks**: How would rollbacks be handled in this implementation? | ||
|
||
# Alternatives Considered | ||
|
||
## Integrate `ETH` transfer into `SuperchainTokenBridge` | ||
|
||
One alternative is to add two new functions to the `SuperchainTokenBridge` contract: `SendETH` and `RelayETH`. | ||
|
||
```solidity | ||
/// @notice Sends ETH to a target address on another chain. | ||
/// @param _to Address to send ETH to. | ||
/// @param _amount Amount of ETH to send. | ||
/// @param _chainId Chain ID of the destination chain. | ||
/// @return msgHash_ Hash of the message sent. | ||
function sendETH(address _to, uint256 _amount, uint256 _chainId) external payable returns (bytes32 msgHash_) { | ||
if (_to == address(0)) revert ZeroAddress(); | ||
ISuperchainWETH(payable(Predeploys.SUPERCHAIN_WETH)).deposit{ value: msg.value }(); | ||
ISuperchainWETH(payable(Predeploys.SUPERCHAIN_WETH)).crosschainBurn(msg.sender, msg.value); | ||
bytes memory message = abi.encodeCall(this.relayETH, (msg.sender, _to, _amount)); | ||
msgHash_ = IL2ToL2CrossDomainMessenger(MESSENGER).sendMessage(_chainId, address(this), message); | ||
emit SendERC20(Predeploys.SUPERCHAIN_WETH, msg.sender, _to, _amount, _chainId); | ||
} | ||
/// @notice Relays ETH received from another chain. | ||
/// @param _from Address of the msg.sender of sendETH on the source chain. | ||
/// @param _to Address to relay ETH to. | ||
/// @param _amount Amount of ETH to relay. | ||
function relayETH(address _from, address _to, uint256 _amount) external { | ||
if (msg.sender != MESSENGER) revert Unauthorized(); | ||
(address crossDomainMessageSender, uint256 source) = | ||
IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageContext(); | ||
if (crossDomainMessageSender != address(this)) revert InvalidCrossDomainSender(); | ||
ISuperchainWETH(payable(Predeploys.SUPERCHAIN_WETH)).crosschainMint(address(this), _amount); | ||
ISuperchainWETH(payable(Predeploys.SUPERCHAIN_WETH)).withdraw(_amount); | ||
new SafeSend{ value: _amount }(payable(_to)); | ||
emit RelayERC20(Predeploys.SUPERCHAIN_WETH, _from, _to, _amount, source); | ||
} | ||
``` | ||
|
||
The advantage of this solution is that `SuperchainTokenBridge`would handle both `ETH` transfers and `SuperchainERC20` transfers, simplifying developer integrations. | ||
|
||
The downside of this approach is that it creates changes to a highly sensitive contract. `SuperchainTokenBridge` has permissions to `mint` and `burn` standard `SuperchainERC20` tokens, so updates must be treated with extreme caution. By going with the proposed solution of adding this to `SuperchainWETH`, security risks are limited to `SuperchainWETH` and do not affect other `SuperchainERC20` tokens. |