From 8fc8a016dd4966aadfaca1117d1abf9107494bce Mon Sep 17 00:00:00 2001 From: tre Date: Mon, 28 Oct 2024 15:33:27 -0700 Subject: [PATCH] feat(protoype): enable superchainWETH native transfers --- .../src/L2/SuperchainWETH.sol | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index 29e179eba82ce..5896b812f6854 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -7,15 +7,22 @@ import { WETH98 } from "src/universal/WETH98.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; +import { SafeSend } from "src/universal/SafeSend.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol"; +error CallerNotL2ToL2CrossDomainMessenger(); + +/// @notice Thrown when attempting to relay a message and the cross domain message sender is not `address(this)` +error InvalidCrossDomainSender(); + /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000024 /// @title SuperchainWETH @@ -23,6 +30,10 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro /// within the superchain. SuperchainWETH can be converted into native ETH on chains that /// do not use a custom gas token. contract SuperchainWETH is WETH98, IERC7802, ISemver { + event SendETH(address indexed from, address indexed to, uint256 amount, uint256 destination); + + event RelayETH(address indexed from, address indexed to, uint256 amount, uint256 source); + /// @notice Semantic version. /// @custom:semver 1.0.0-beta.10 string public constant version = "1.0.0-beta.10"; @@ -98,4 +109,51 @@ contract SuperchainWETH is WETH98, IERC7802, ISemver { return _interfaceId == type(IERC7802).interfaceId || _interfaceId == type(IERC20).interfaceId || _interfaceId == type(IERC165).interfaceId; } + + /// @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); + } }