Skip to content

Commit

Permalink
feat: add L2 standrad bridge interop contract
Browse files Browse the repository at this point in the history
  • Loading branch information
agusduha committed Aug 5, 2024
1 parent 6cb07ee commit 64dc6d4
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 1 deletion.
96 changes: 96 additions & 0 deletions packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { Predeploys } from "src/libraries/Predeploys.sol";
import { L2StandardBridge } from "./L2StandardBridge.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

/// @notice Thrown when the decimals of the tokens are not the same.
error InvalidDecimals();

/// @notice Thrown when the legacy address is not found in the OptimismMintableERC20Factory.
error InvalidLegacyAddress();

/// @notice Thrown when the SuperchainERC20 address is not found in the SuperchainERC20Factory.
error InvalidSuperchainAddress();

/// @notice Thrown when the remote addresses of the tokens are not the same.
error InvalidTokenPair();

// TODO: Use OptimismMintableERC20Factory contract instead of interface
interface IOptimismMintableERC20Factory {
function deployments(address) external view returns (address);
}

// TODO: Move to a separate file
interface ISuperchainERC20Factory {
function deployments(address) external view returns (address);
}

// TODO: Use an existing interface with `mint` and `burn`?
interface MintableAndBurnable is IERC20 {
function mint(address, uint256) external;
function burn(address, uint256) external;
}

/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000010
/// @title L2StandardBridgeInterop
/// @notice The L2StandardBridgeInterop is an extension of the L2StandardBridge that allows for
/// the conversion of tokens between legacy tokens (OptimismMintableERC20 or StandardL2ERC20)
/// and SuperchainERC20 tokens.
contract L2StandardBridgeInterop is L2StandardBridge {
/// @notice Emitted when a conversion is made.
/// @param from The token being converted from.
/// @param to The token being converted to.
/// @param caller The caller of the conversion.
/// @param amount The amount of tokens being converted.
event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount);

/// @notice Converts `amount` of `from` token to `to` token.
/// @param _from The token being converted from.
/// @param _to The token being converted to.
/// @param _amount The amount of tokens being converted.
function convert(address _from, address _to, uint256 _amount) external {
_validatePair(_from, _to);

MintableAndBurnable(_from).burn(msg.sender, _amount);
MintableAndBurnable(_to).mint(msg.sender, _amount);

emit Converted(_from, _to, msg.sender, _amount);
}

/// @notice Validates the pair of tokens.
/// @param _from The token being converted from.
/// @param _to The token being converted to.
function _validatePair(address _from, address _to) internal view {
// 1. Decimals check
if (IERC20Metadata(_from).decimals() != IERC20Metadata(_to).decimals()) revert InvalidDecimals();

// Order tokens for factory validation
if (_isOptimismMintableERC20(_from)) {
_validateFactories(_from, _to);
} else {
_validateFactories(_to, _from);
}
}

/// @notice Validates that the tokens are deployed by the correct factory.
/// @param _legacyAddr The legacy token address (OptimismMintableERC20 or StandardL2ERC20).
/// @param _superAddr The SuperchainERC20 address.
function _validateFactories(address _legacyAddr, address _superAddr) internal view {
// 2. Valid legacy check
address _legacyRemoteToken =
IOptimismMintableERC20Factory(Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY).deployments(_legacyAddr);
if (_legacyRemoteToken == address(0)) revert InvalidLegacyAddress();

// 3. Valid SuperchainERC20 check
address _superRemoteToken =
ISuperchainERC20Factory(Predeploys.OPTIMISM_SUPERCHAIN_ERC20_FACTORY).deployments(_superAddr);
if (_superRemoteToken == address(0)) revert InvalidSuperchainAddress();

// 4. Same remote address check
if (_legacyRemoteToken != _superRemoteToken) revert InvalidTokenPair();
}
}
7 changes: 6 additions & 1 deletion packages/contracts-bedrock/src/libraries/Predeploys.sol
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ library Predeploys {
/// @notice Address of the ETHLiquidity predeploy.
address internal constant ETH_LIQUIDITY = 0x4200000000000000000000000000000000000025;

/// @notice Address of the OptimismSuperchainERC20Factory predeploy.
address internal constant OPTIMISM_SUPERCHAIN_ERC20_FACTORY = 0x4200000000000000000000000000000000000026;

/// @notice Returns the name of the predeploy at the given address.
function getName(address _addr) internal pure returns (string memory out_) {
require(isPredeployNamespace(_addr), "Predeploys: address must be a predeploy");
Expand Down Expand Up @@ -123,6 +126,7 @@ library Predeploys {
if (_addr == L2_TO_L2_CROSS_DOMAIN_MESSENGER) return "L2ToL2CrossDomainMessenger";
if (_addr == SUPERCHAIN_WETH) return "SuperchainWETH";
if (_addr == ETH_LIQUIDITY) return "ETHLiquidity";
if (_addr == OPTIMISM_SUPERCHAIN_ERC20_FACTORY) return "OptimismSuperchainERC20Factory";
revert("Predeploys: unnamed predeploy");
}

Expand All @@ -140,7 +144,8 @@ library Predeploys {
|| _addr == OPTIMISM_MINTABLE_ERC721_FACTORY || _addr == PROXY_ADMIN || _addr == BASE_FEE_VAULT
|| _addr == L1_FEE_VAULT || _addr == SCHEMA_REGISTRY || _addr == EAS || _addr == GOVERNANCE_TOKEN
|| (_useInterop && _addr == CROSS_L2_INBOX) || (_useInterop && _addr == L2_TO_L2_CROSS_DOMAIN_MESSENGER)
|| (_useInterop && _addr == SUPERCHAIN_WETH) || (_useInterop && _addr == ETH_LIQUIDITY);
|| (_useInterop && _addr == SUPERCHAIN_WETH) || (_useInterop && _addr == ETH_LIQUIDITY)
|| (_useInterop && _addr == OPTIMISM_SUPERCHAIN_ERC20_FACTORY);
}

function isPredeployNamespace(address _addr) internal pure returns (bool) {
Expand Down

0 comments on commit 64dc6d4

Please sign in to comment.