Skip to content

Commit

Permalink
feat(protoype): enable superchainWETH native transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
tremarkley committed Nov 19, 2024
1 parent f94151b commit 54cbef5
Show file tree
Hide file tree
Showing 5 changed files with 385 additions and 6 deletions.
119 changes: 119 additions & 0 deletions packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,53 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_from",
"type": "address"
},
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "relayETH",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_chainId",
"type": "uint256"
}
],
"name": "sendETH",
"outputs": [
{
"internalType": "bytes32",
"name": "msgHash_",
"type": "bytes32"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -349,6 +396,68 @@
"name": "Deposit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "source",
"type": "uint256"
}
],
"name": "RelayETH",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "destination",
"type": "uint256"
}
],
"name": "SendETH",
"type": "event"
},
{
"anonymous": false,
"inputs": [
Expand Down Expand Up @@ -393,6 +502,11 @@
"name": "Withdrawal",
"type": "event"
},
{
"inputs": [],
"name": "InvalidCrossDomainSender",
"type": "error"
},
{
"inputs": [],
"name": "NotCustomGasToken",
Expand All @@ -402,5 +516,10 @@
"inputs": [],
"name": "Unauthorized",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
}
]
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@
"sourceCodeHash": "0x617aa994f659c5d8ebd54128d994f86f5b175ceca095b024b8524a7898e8ae62"
},
"src/L2/SuperchainWETH.sol": {
"initCodeHash": "0x5aef986a7c9c102b1e9b3068e2a2b66adce0a71dd5f39e03694622bf494f8d97",
"sourceCodeHash": "0xa62101a23b860e97f393027c898082a1c73d50679eceb6c6793844af29702359"
"initCodeHash": "0x08b38d8ed5ade481fd6b06868aea64ffaff31d2d45b1c305f59da7ab899931fc",
"sourceCodeHash": "0x9c34e7e92ce65cbb39189a201ccd495fefe17ae1586fc1f494c57209c3a66573"
},
"src/L2/WETH.sol": {
"initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17",
Expand Down
69 changes: 66 additions & 3 deletions packages/contracts-bedrock/src/L2/SuperchainWETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ pragma solidity 0.8.15;
import { WETH98 } from "src/universal/WETH98.sol";

// Libraries
import { NotCustomGasToken, Unauthorized, ZeroAddress } from "src/libraries/errors/CommonErrors.sol";
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";

/// @custom:proxied true
/// @custom:predeploy 0x4200000000000000000000000000000000000024
Expand All @@ -23,9 +25,26 @@ 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 {
/// @notice Thrown when attempting to relay a message and the cross domain message sender is not SuperchainWETH.
error InvalidCrossDomainSender();

/// @notice Emitted when ETH is sent from one chain to another.
/// @param from Address of the sender.
/// @param to Address of the recipient.
/// @param amount Amount of ETH sent.
/// @param destination Chain ID of the destination chain.
event SendETH(address indexed from, address indexed to, uint256 amount, uint256 destination);

/// @notice Emitted whenever ETH is successfully relayed on this chain.
/// @param from Address of the msg.sender of sendETH on the source chain.
/// @param to Address of the recipient.
/// @param amount Amount of ETH relayed.
/// @param source Chain ID of the source chain.
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";
/// @custom:semver 1.0.0-beta.11
string public constant version = "1.0.0-beta.11";

/// @inheritdoc WETH98
function deposit() public payable override {
Expand Down Expand Up @@ -98,4 +117,48 @@ 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) external payable returns (bytes32 msgHash_) {
if (_to == address(0)) revert ZeroAddress();

if (IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) {
revert NotCustomGasToken();
}

IETHLiquidity(Predeploys.ETH_LIQUIDITY).burn{ value: msg.value }();

msgHash_ = 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 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 ETH to.
/// @param _amount Amount of ETH to relay.
function relayETH(address _from, address _to, uint256 _amount) external {
if (msg.sender != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert Unauthorized();

(address crossDomainMessageSender, uint256 source) =
IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER).crossDomainMessageContext();

if (crossDomainMessageSender != address(this)) revert InvalidCrossDomainSender();

if (IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) {
revert NotCustomGasToken();
}

IETHLiquidity(Predeploys.ETH_LIQUIDITY).mint(_amount);

new SafeSend{ value: _amount }(payable(_to));

emit RelayETH(_from, _to, _amount, source);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,18 @@ import { ISemver } from "src/universal/interfaces/ISemver.sol";
interface ISuperchainWETH is IWETH98, IERC7802, ISemver {
error Unauthorized();
error NotCustomGasToken();
error InvalidCrossDomainSender();
error ZeroAddress();

event SendETH(address indexed from, address indexed to, uint256 amount, uint256 destination);

event RelayETH(address indexed from, address indexed to, uint256 amount, uint256 source);

function balanceOf(address src) external view returns (uint256);
function withdraw(uint256 _amount) external;
function supportsInterface(bytes4 _interfaceId) external view returns (bool);
function sendETH(address _to, uint256 _chainId) external payable returns (bytes32 msgHash_);
function relayETH(address _from, address _to, uint256 _amount) external;

function __constructor__() external;
}
Loading

0 comments on commit 54cbef5

Please sign in to comment.