Skip to content

Commit

Permalink
refactor: optimism superchain erc20 redesign (#62)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
0xDiscotech authored Sep 26, 2024
1 parent ae024a5 commit 2a22161
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 562 deletions.
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@
"sourceCodeHash": "0x4b806cc85cead74c8df34ab08f4b6c6a95a1a387a335ec8a7cb2de4ea4e1cf41"
},
"src/L2/OptimismSuperchainERC20.sol": {
"initCodeHash": "0x4fd71b5352b78d51d39625b6defa77a75be53067b32f3cba86bd17a46917adf9",
"sourceCodeHash": "0xad3934ea533544b3c130c80be26201354af85f9166cb2ce54d96e5e383ebb5c1"
"initCodeHash": "0x9b48e5b8271d9b5d4407bf1a7327842844b4bd858123ffa6da0c55ad9d3b9d8e",
"sourceCodeHash": "0xaf5702655736e9c4b480a0bb4f0f2220f09834b2292f8e5419b5b245e0a20300"
},
"src/L2/OptimismSuperchainERC20Beacon.sol": {
"initCodeHash": "0x99ce8095b23c124850d866cbc144fee6cee05dbc6bb5d83acadfe00b90cf42c7",
Expand All @@ -136,8 +136,8 @@
"sourceCodeHash": "0xb11ce94fd6165d8ca86eebafc7235e0875380d1a5d4e8b267ff0c6477083b21c"
},
"src/L2/SuperchainWETH.sol": {
"initCodeHash": "0xd8766c7ab41d34d935febf5b48289f947804634bde38f8e346075b9f2d867275",
"sourceCodeHash": "0x6c1691c0fb5c86f1fd67e23495725c2cd86567556602e8cc0f28104ad6114bf4"
"initCodeHash": "0x702ff6dc90e7e02085e95e3510590cce9bf44a7ea06bfbb8f7a47e203a8809b2",
"sourceCodeHash": "0x823ded4da0dc1f44bc87b5e46d0a1c90c76f76e0f36c294c5410c4755886c925"
},
"src/L2/WETH.sol": {
"initCodeHash": "0xfb253765520690623f177941c2cd9eba23e4c6d15063bccdd5e98081329d8956",
Expand Down
120 changes: 1 addition & 119 deletions packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json
Original file line number Diff line number Diff line change
Expand Up @@ -236,29 +236,6 @@
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_from",
"type": "address"
},
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "relayERC20",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "remoteToken",
Expand All @@ -272,29 +249,6 @@
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_to",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_chainId",
"type": "uint256"
}
],
"name": "sendERC20",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
Expand Down Expand Up @@ -482,68 +436,6 @@
"name": "Mint",
"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": "RelayERC20",
"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": "SendERC20",
"type": "event"
},
{
"anonymous": false,
"inputs": [
Expand Down Expand Up @@ -579,11 +471,6 @@
"name": "AllowanceUnderflow",
"type": "error"
},
{
"inputs": [],
"name": "CallerNotL2ToL2CrossDomainMessenger",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientAllowance",
Expand All @@ -594,11 +481,6 @@
"name": "InsufficientBalance",
"type": "error"
},
{
"inputs": [],
"name": "InvalidCrossDomainSender",
"type": "error"
},
{
"inputs": [],
"name": "InvalidInitialization",
Expand All @@ -616,7 +498,7 @@
},
{
"inputs": [],
"name": "OnlyBridge",
"name": "OnlyAuthorizedBridge",
"type": "error"
},
{
Expand Down
12 changes: 11 additions & 1 deletion packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json
Original file line number Diff line number Diff line change
Expand Up @@ -408,14 +408,24 @@
"name": "Withdrawal",
"type": "event"
},
{
"inputs": [],
"name": "CallerNotL2ToL2CrossDomainMessenger",
"type": "error"
},
{
"inputs": [],
"name": "InvalidCrossDomainSender",
"type": "error"
},
{
"inputs": [],
"name": "NotCustomGasToken",
"type": "error"
},
{
"inputs": [],
"name": "Unauthorized",
"name": "ZeroAddress",
"type": "error"
}
]
31 changes: 10 additions & 21 deletions packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@ import { IOptimismSuperchainERC20Extension } from "src/L2/interfaces/IOptimismSu
import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { ERC165 } from "@openzeppelin/contracts-v5/utils/introspection/ERC165.sol";
import { ERC20 } from "@solady/tokens/ERC20.sol";
import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol";
import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol";
import { ERC165 } from "@openzeppelin/contracts-v5/utils/introspection/ERC165.sol";

/// @notice Thrown when attempting to mint or burn tokens and the function caller is not the StandardBridge.
error OnlyBridge();

/// @custom:proxied true
/// @title OptimismSuperchainERC20
Expand All @@ -21,16 +17,7 @@ error OnlyBridge();
/// token, turning it fungible and interoperable across the superchain. Likewise, it also enables the inverse
/// conversion path.
/// Moreover, it builds on top of the L2ToL2CrossDomainMessenger for both replay protection and domain binding.
contract OptimismSuperchainERC20 is
IOptimismSuperchainERC20Extension,
SuperchainERC20,
ISemver,
Initializable,
ERC165
{
/// @notice Address of the StandardBridge Predeploy.
address internal constant BRIDGE = Predeploys.L2_STANDARD_BRIDGE;

contract OptimismSuperchainERC20 is ERC20, Initializable, ERC165, IOptimismSuperchainERC20Extension, ISemver {
/// @notice Storage slot that the OptimismSuperchainERC20Metadata struct is stored at.
/// keccak256(abi.encode(uint256(keccak256("optimismSuperchainERC20.metadata")) - 1)) & ~bytes32(uint256(0xff));
bytes32 internal constant OPTIMISM_SUPERCHAIN_ERC20_METADATA_SLOT =
Expand All @@ -57,14 +44,16 @@ contract OptimismSuperchainERC20 is
}

/// @notice A modifier that only allows the bridge to call
modifier onlyBridge() {
if (msg.sender != BRIDGE) revert OnlyBridge();
modifier onlyAuthorizedBridge() {
if (msg.sender != Predeploys.L2_STANDARD_BRIDGE && msg.sender != Predeploys.SUPERCHAIN_ERC20_BRIDGE) {
revert OnlyAuthorizedBridge();
}
_;
}

/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.2
string public constant version = "1.0.0-beta.2";
/// @custom:semver 1.0.0-beta.3
string public constant version = "1.0.0-beta.3";

/// @notice Constructs the OptimismSuperchainERC20 contract.
constructor() {
Expand Down Expand Up @@ -95,7 +84,7 @@ contract OptimismSuperchainERC20 is
/// @notice Allows the L2StandardBridge to mint tokens.
/// @param _to Address to mint tokens to.
/// @param _amount Amount of tokens to mint.
function mint(address _to, uint256 _amount) external virtual onlyBridge {
function mint(address _to, uint256 _amount) external virtual onlyAuthorizedBridge {
if (_to == address(0)) revert ZeroAddress();

_mint(_to, _amount);
Expand All @@ -106,7 +95,7 @@ contract OptimismSuperchainERC20 is
/// @notice Allows the L2StandardBridge to burn tokens.
/// @param _from Address to burn tokens from.
/// @param _amount Amount of tokens to burn.
function burn(address _from, uint256 _amount) external virtual onlyBridge {
function burn(address _from, uint256 _amount) external virtual onlyAuthorizedBridge {
if (_from == address(0)) revert ZeroAddress();

_burn(_from, _amount);
Expand Down
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/src/L2/SuperchainERC20.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;

import { ISuperchainERC20Extensions, ISuperchainERC20Errors } from "src/L2/interfaces/ISuperchainERC20.sol";
import { ISuperchainERC20Extensions } from "src/L2/interfaces/ISuperchainERC20.sol";
import { ERC20 } from "@solady/tokens/ERC20.sol";
import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
Expand All @@ -10,7 +10,7 @@ import { Predeploys } from "src/libraries/Predeploys.sol";
/// @notice SuperchainERC20 is a standard extension of the base ERC20 token contract that unifies ERC20 token
/// bridging to make it fungible across the Superchain. It builds on top of the L2ToL2CrossDomainMessenger for
/// both replay protection and domain binding.
abstract contract SuperchainERC20 is ISuperchainERC20Extensions, ISuperchainERC20Errors, ERC20 {
abstract contract SuperchainERC20 is ERC20, ISuperchainERC20Extensions {
/// @notice Address of the L2ToL2CrossDomainMessenger Predeploy.
address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER;

Expand Down
8 changes: 4 additions & 4 deletions packages/contracts-bedrock/src/L2/SuperchainWETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol";
/// do not use a custom gas token.
contract SuperchainWETH is WETH98, ISuperchainERC20Extensions, ISemver {
/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.4
string public constant version = "1.0.0-beta.4";
/// @custom:semver 1.0.0-beta.5
string public constant version = "1.0.0-beta.5";

/// @inheritdoc WETH98
function deposit() public payable override {
Expand Down Expand Up @@ -61,8 +61,8 @@ contract SuperchainWETH is WETH98, ISuperchainERC20Extensions, ISemver {
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 Unauthorized();
if (messenger.crossDomainMessageSender() != address(this)) revert Unauthorized();
if (msg.sender != address(messenger)) revert CallerNotL2ToL2CrossDomainMessenger();
if (messenger.crossDomainMessageSender() != address(this)) revert InvalidCrossDomainSender();

// Mint from ETHLiquidity contract.
if (!IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@ pragma solidity ^0.8.0;

// Interfaces
import { IERC20Solady } from "src/vendor/interfaces/IERC20Solady.sol";
import { ISuperchainERC20Extensions, ISuperchainERC20Errors } from "src/L2/interfaces/ISuperchainERC20.sol";

/// @title IOptimismSuperchainERC20Errors
/// @notice Interface containing the errors added in the OptimismSuperchainERC20 implementation.
interface IOptimismSuperchainERC20Errors {
/// @notice Thrown when attempting to perform an operation and the account is the zero address.
error ZeroAddress();

/// @notice Thrown when attempting to mint or burn tokens and the function caller is not the StandardBridge or the
/// SuperchainERC20Bridge.
error OnlyAuthorizedBridge();
}

/// @title IOptimismSuperchainERC20Extension
/// @notice This interface is available on the OptimismSuperchainERC20 contract.
/// We declare it as a separate interface so that it can be used in
/// custom implementations of SuperchainERC20.
interface IOptimismSuperchainERC20Extension is ISuperchainERC20Extensions, ISuperchainERC20Errors {
interface IOptimismSuperchainERC20Extension is IOptimismSuperchainERC20Errors {
/// @notice Emitted whenever tokens are minted for an account.
/// @param account Address of the account tokens are being minted for.
/// @param amount Amount of tokens minted.
Expand Down
33 changes: 16 additions & 17 deletions packages/contracts-bedrock/src/L2/interfaces/ISuperchainERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,25 @@ pragma solidity ^0.8.0;
// Interfaces
import { IERC20Solady } from "src/vendor/interfaces/IERC20Solady.sol";

/// @title ISuperchainERC20Errors
/// @notice Interface containing the errors added in the SuperchainERC20 implementation.
interface ISuperchainERC20Errors {
/// @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 Thrown when attempting to perform an operation and the account is the zero address.
error ZeroAddress();
}

/// @title ISuperchainERC20Extensions
/// @notice Interface for the extensions to the ERC20 standard that are used by SuperchainERC20.
/// Exists in case developers are already importing the ERC20 interface separately and
/// importing the full SuperchainERC20 interface would cause conflicting imports.
interface ISuperchainERC20Extensions {
interface ISuperchainERC20Extensions is ISuperchainERC20Errors {
/// @notice Emitted when tokens are sent from one chain to another.
/// @param from Address of the sender.
/// @param to Address of the recipient.
Expand Down Expand Up @@ -36,21 +50,6 @@ interface ISuperchainERC20Extensions {
function relayERC20(address _from, address _to, uint256 _amount) external;
}

/// @title ISuperchainERC20Errors
/// @notice Interface containing the errors added in the SuperchainERC20 implementation.
interface ISuperchainERC20Errors {
/// @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 this
/// SuperchainERC20.
error InvalidCrossDomainSender();

/// @notice Thrown when attempting to perform an operation and the account is the zero address.
error ZeroAddress();
}

/// @title ISuperchainERC20
/// @notice Combines Solady's ERC20 interface with the SuperchainERC20Extensions interface.
interface ISuperchainERC20 is IERC20Solady, ISuperchainERC20Extensions, ISuperchainERC20Errors { }
interface ISuperchainERC20 is IERC20Solady, ISuperchainERC20Extensions { }
Loading

0 comments on commit 2a22161

Please sign in to comment.