Skip to content

Commit

Permalink
feat: SuperchainWETH redesign (ethereum-optimism#12514)
Browse files Browse the repository at this point in the history
* feat: SuperchainWETH redesign (#101)

* feat: add superchain erc20 bridge (#61)

* feat: add superchain erc20 bridge

* fix: interfaces and versions

* refactor: optimism superchain erc20 redesign (#62)

* refactor: use oz upgradeable erc20 as dependency

* chore: update interfaces

* fix: tests based on changes

* refactor: remove op as dependency

* feat: add check for supererc20 bridge on modifier

* chore: update tests and interfaces

* chore: update stack vars name on test

* chore: remove empty gitmodules file

* chore: update superchain weth errors

* test: add superchain erc20 bridge tests (#65)

* test: add superchain erc20 bridge tests

* test: add optimism superchain erc20 beacon tests

* test: remove unnecessary test

* test: tests fixes

* test: tests fixes

* chore: update missing bridge on natspec (ethereum-optimism#69)

* chore: update missing bridge on natspec

* fix: natspecs

---------

Co-authored-by: agusduha <[email protected]>

* fix: remove superchain erc20 base (#70)

* refactor: update isuperchainweth (#71)


---------

Co-authored-by: agusduha <[email protected]>

* feat: rename mint/burn and add SuperchainERC20 (ethereum-optimism#74)

* refactor: rename mint and burn functions on superchain erc20

* chore: rename optimism superchain erc20 to superchain erc20

* feat: create optimism superchain erc20 contract

* chore: update natspec and errors

* fix: superchain erc20 tests

* refactor: make superchain erc20 abstract

* refactor: move storage and erc20 metadata functions to implementation

* chore: update interfaces

* chore: update superchain erc20 events

* fix: tests

* fix: natspecs

* fix: add semmver lock and snapshots

* fix: remove unused imports

* fix: natspecs

---------

Co-authored-by: 0xDiscotech <[email protected]>

* fix: refactor zero check (ethereum-optimism#76)

* fix: pre pr

* fix: semver natspec check failure (#79)

* fix: semver natspec check failure

* fix: ignore mock contracts in semver natspec script

* fix: error message

* feat: add crosschain erc20 interface (#80)

* feat: add crosschain erc20 interface

* fix: refactor interfaces

* fix: superchain bridge natspec (#83)

* fix: superchain weth natspec (#84)

Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xParticle <[email protected]>
Co-authored-by: gotzenx <[email protected]>

* fix: stop inheriting superchain interfaces (#85)

* fix: stop inheriting superchain interfaces

* fix: move events and erros into the implementation

* fix: make superchainERC20 inherits from crosschainERC20

* fix: superchain bridge rename (#86)

* fix: fee vault compiler error (ethereum-optimism#87)

* fix: remove unused imports

* fix: refactor common errors (#90)

* fix: refactor common errors

* fix: remove unused version

* fix: reuse unauthorized error (ethereum-optimism#92)

* fix: superchain erc20 factory conflicts

* fix: rename crosschain functions (ethereum-optimism#94)

* feat: superweth redesign

* fix: pr fixes

* fix: fixes post merge

---------

Co-authored-by: Disco <[email protected]>
Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xParticle <[email protected]>
Co-authored-by: gotzenx <[email protected]>

* fix: SuperchainWETH redesign fixes (#110)

* fix: superchainWETH redesign fixes

* fix: withdraw arg

* fix: fix revert in SuperchainWETH tests (#112)

---------

Co-authored-by: AgusDuha <[email protected]>
Co-authored-by: Disco <[email protected]>
Co-authored-by: 0xng <[email protected]>
Co-authored-by: 0xParticle <[email protected]>
Co-authored-by: agusduha <[email protected]>
  • Loading branch information
6 people authored Oct 22, 2024
1 parent 76beff3 commit 574aba7
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 447 deletions.
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,8 @@
"sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036"
},
"src/L2/SuperchainWETH.sol": {
"initCodeHash": "0x50f6ea9bfe650fcf792e98e44b1bf66c036fd0e6d4b753da680253d7d8609816",
"sourceCodeHash": "0x82d03262decf52d5954d40bca8703f96a0f3ba7accf6c1d75292856c2f34cf8f"
"initCodeHash": "0x09c7efed7d6c8ae5981f6e7a75c7b8c675f73d679265d15a010844ad9b41fa9b",
"sourceCodeHash": "0x8d7612a71deaadfb324c4136673df96019211292ff54494fa4b7724e2e5dd22a"
},
"src/L2/WETH.sol": {
"initCodeHash": "0xfb253765520690623f177941c2cd9eba23e4c6d15063bccdd5e98081329d8956",
Expand Down
131 changes: 46 additions & 85 deletions packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,42 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_from",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "crosschainBurn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "crosschainMint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
Expand Down Expand Up @@ -107,52 +143,6 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "dst",
"type": "address"
},
{
"internalType": "uint256",
"name": "wad",
"type": "uint256"
}
],
"name": "relayERC20",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "dst",
"type": "address"
},
{
"internalType": "uint256",
"name": "wad",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
}
],
"name": "sendERC20",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
Expand Down Expand Up @@ -249,7 +239,7 @@
"inputs": [
{
"internalType": "uint256",
"name": "wad",
"name": "_amount",
"type": "uint256"
}
],
Expand Down Expand Up @@ -289,28 +279,22 @@
{
"indexed": true,
"internalType": "address",
"name": "dst",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "wad",
"name": "amount",
"type": "uint256"
}
],
"name": "Deposit",
"name": "CrosschainBurnt",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
Expand All @@ -322,15 +306,9 @@
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "source",
"type": "uint256"
}
],
"name": "RelayERC20",
"name": "CrosschainMinted",
"type": "event"
},
{
Expand All @@ -339,29 +317,17 @@
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"name": "dst",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "destination",
"name": "wad",
"type": "uint256"
}
],
"name": "SendERC20",
"name": "Deposit",
"type": "event"
},
{
Expand Down Expand Up @@ -410,17 +376,12 @@
},
{
"inputs": [],
"name": "CallerNotL2ToL2CrossDomainMessenger",
"type": "error"
},
{
"inputs": [],
"name": "InvalidCrossDomainSender",
"name": "NotCustomGasToken",
"type": "error"
},
{
"inputs": [],
"name": "NotCustomGasToken",
"name": "Unauthorized",
"type": "error"
}
]
94 changes: 41 additions & 53 deletions packages/contracts-bedrock/src/L2/SuperchainWETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ import { Predeploys } from "src/libraries/Predeploys.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 { ISuperchainWETH } from "src/L2/interfaces/ISuperchainWETH.sol";
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol";
import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol";

/// @custom:proxied true
/// @custom:predeploy 0x4200000000000000000000000000000000000024
/// @title SuperchainWETH
/// @notice SuperchainWETH is a version of WETH that can be freely transfrered between chains
/// within the superchain. SuperchainWETH can be converted into native ETH on chains that
/// do not use a custom gas token.
contract SuperchainWETH is WETH98, ISuperchainWETH, ISemver {
contract SuperchainWETH is WETH98, ICrosschainERC20, ISemver {
/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.6
string public constant version = "1.0.0-beta.6";
/// @custom:semver 1.0.0-beta.7
string public constant version = "1.0.0-beta.7";

/// @inheritdoc WETH98
function deposit() public payable override {
Expand All @@ -32,68 +32,56 @@ contract SuperchainWETH is WETH98, ISuperchainWETH, ISemver {
}

/// @inheritdoc WETH98
function withdraw(uint256 wad) public override {
function withdraw(uint256 _amount) public override {
if (IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) revert NotCustomGasToken();
super.withdraw(wad);
super.withdraw(_amount);
}

/// @inheritdoc ISuperchainWETH
function sendERC20(address dst, uint256 wad, uint256 chainId) public {
// Burn from user's balance.
_burn(msg.sender, wad);

// Burn to ETHLiquidity contract.
if (!IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) {
IETHLiquidity(Predeploys.ETH_LIQUIDITY).burn{ value: wad }();
}

// Send message to other chain.
IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER).sendMessage({
_destination: chainId,
_target: address(this),
_message: abi.encodeCall(this.relayERC20, (msg.sender, dst, wad))
});
/// @notice Mints WETH to an address.
/// @param _to The address to mint WETH to.
/// @param _amount The amount of WETH to mint.
function _mint(address _to, uint256 _amount) internal {
balanceOf[_to] += _amount;
emit Transfer(address(0), _to, _amount);
}

// Emit event.
emit SendERC20(msg.sender, dst, wad, chainId);
/// @notice Burns WETH from an address.
/// @param _from The address to burn WETH from.
/// @param _amount The amount of WETH to burn.
function _burn(address _from, uint256 _amount) internal {
balanceOf[_from] -= _amount;
emit Transfer(_from, address(0), _amount);
}

/// @inheritdoc ISuperchainWETH
function relayERC20(address from, address dst, uint256 wad) 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();
/// @notice Allows the SuperchainTokenBridge to mint tokens.
/// @param _to Address to mint tokens to.
/// @param _amount Amount of tokens to mint.
function crosschainMint(address _to, uint256 _amount) external {
if (msg.sender != Predeploys.SUPERCHAIN_TOKEN_BRIDGE) revert Unauthorized();

_mint(_to, _amount);

// Mint from ETHLiquidity contract.
if (!IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) {
IETHLiquidity(Predeploys.ETH_LIQUIDITY).mint(wad);
IETHLiquidity(Predeploys.ETH_LIQUIDITY).mint(_amount);
}

// Get source chain ID.
uint256 source = messenger.crossDomainMessageSource();
emit CrosschainMinted(_to, _amount);
}

// Mint to user's balance.
_mint(dst, wad);
/// @notice Allows the SuperchainTokenBridge to burn tokens.
/// @param _from Address to burn tokens from.
/// @param _amount Amount of tokens to burn.
function crosschainBurn(address _from, uint256 _amount) external {
if (msg.sender != Predeploys.SUPERCHAIN_TOKEN_BRIDGE) revert Unauthorized();

// Emit event.
emit RelayERC20(from, dst, wad, source);
}
_burn(_from, _amount);

/// @notice Mints WETH to an address.
/// @param guy The address to mint WETH to.
/// @param wad The amount of WETH to mint.
function _mint(address guy, uint256 wad) internal {
balanceOf[guy] += wad;
emit Transfer(address(0), guy, wad);
}
// Burn to ETHLiquidity contract.
if (!IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) {
IETHLiquidity(Predeploys.ETH_LIQUIDITY).burn{ value: _amount }();
}

/// @notice Burns WETH from an address.
/// @param guy The address to burn WETH from.
/// @param wad The amount of WETH to burn.
function _burn(address guy, uint256 wad) internal {
require(balanceOf[guy] >= wad);
balanceOf[guy] -= wad;
emit Transfer(guy, address(0), wad);
emit CrosschainBurnt(_from, _amount);
}
}
41 changes: 5 additions & 36 deletions packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,12 @@
pragma solidity ^0.8.0;

import { IWETH } from "src/universal/interfaces/IWETH.sol";
import { ICrosschainERC20 } from "src/L2/interfaces/ICrosschainERC20.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol";

interface ISuperchainWETH {
/// @notice Thrown when attempting a deposit or withdrawal and the chain uses a custom gas token.
interface ISuperchainWETH is IWETH, ICrosschainERC20, ISemver {
error Unauthorized();
error NotCustomGasToken();

/// @notice Thrown when attempting to relay a message and the function caller (msg.sender) is not
/// L2ToL2CrossDomainMessenger.
error CallerNotL2ToL2CrossDomainMessenger();

/// @notice Thrown when attempting to relay a message and the cross domain message sender is not `address(this)`
error InvalidCrossDomainSender();

/// @notice Emitted whenever tokens are successfully relayed on this chain.
/// @param from Address of the msg.sender of sendERC20 on the source chain.
/// @param to Address of the recipient.
/// @param amount Amount of tokens relayed.
/// @param source Chain ID of the source chain.
event RelayERC20(address indexed from, address indexed to, uint256 amount, uint256 source);

/// @notice Emitted when tokens are sent from one chain to another.
/// @param from Address of the sender.
/// @param to Address of the recipient.
/// @param amount Number of tokens sent.
/// @param destination Chain ID of the destination chain.
event SendERC20(address indexed from, address indexed to, uint256 amount, uint256 destination);

/// @notice Sends tokens to some target address on another chain.
/// @param _dst Address to send tokens to.
/// @param _wad Amount of tokens to send.
/// @param _chainId Chain ID of the destination chain.
function sendERC20(address _dst, uint256 _wad, uint256 _chainId) external;

/// @notice Relays tokens received from another chain.
/// @param _from Address of the msg.sender of sendERC20 on the source chain.
/// @param _dst Address to relay tokens to.
/// @param _wad Amount of tokens to relay.
function relayERC20(address _from, address _dst, uint256 _wad) external;
function __constructor__() external;
}

interface ISuperchainWETHERC20 is IWETH, ISuperchainWETH { }
Loading

0 comments on commit 574aba7

Please sign in to comment.