Skip to content

Commit

Permalink
Merge pull request #17 from morpho-org/review/merlin-optimism
Browse files Browse the repository at this point in the history
Review/merlin optimism
  • Loading branch information
Jean-Grimal authored Oct 18, 2024
2 parents 7e1de59 + 04ddd55 commit c02a6cc
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 89 deletions.
2 changes: 1 addition & 1 deletion src/ERC20DelegatesUpgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {EIP712Upgradeable} from
import {Initializable} from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";

/// @title ERC20DelegatesUpgradeable
/// @author Morpho Labs
/// @author Morpho Association
/// @custom:contact security@morpho.org
/// @dev Extension of ERC20 to support token delegation.
///
Expand Down
2 changes: 1 addition & 1 deletion src/MorphoToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import {UUPSUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";

/// @title MorphoToken
/// @author Morpho Labs
/// @author Morpho Association
/// @custom:contact security@morpho.org
/// @notice The MORPHO Token contract.
contract MorphoToken is ERC20DelegatesUpgradeable, ERC20PermitUpgradeable, Ownable2StepUpgradeable, UUPSUpgradeable {
Expand Down
7 changes: 1 addition & 6 deletions src/MorphoTokenOptimism.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {ERC20PermitUpgradeable} from
import {UUPSUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";

/// @title MorphoToken
/// @author Morpho Labs
/// @author Morpho Association
/// @custom:contact security@morpho.org
/// @notice The MORPHO Token contract for Optimism networks.
contract MorphoTokenOptimism is
Expand All @@ -27,11 +27,6 @@ contract MorphoTokenOptimism is
/// @dev The symbol of the token.
string internal constant SYMBOL = "MORPHO";

/* ERRORS */

/// @notice Reverts if the address is the zero address.
error ZeroAddress();

/* PUBLIC */

/// @notice Initializes the contract.
Expand Down
120 changes: 57 additions & 63 deletions src/OptimismMintableERC20Upgradeable.sol
Original file line number Diff line number Diff line change
@@ -1,124 +1,118 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.27;

import {ERC20Upgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol";
import {IOptimismMintableERC20} from "./interfaces/IOptimismMintableERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ILegacyMintableERC20, IOptimismMintableERC20} from "./interfaces/IOptimismMintableERC20.sol";
import {Initializable} from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {ERC20Upgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol";

/// @title OptimismMintableERC20
/// @author Morpho Labs
/// @author Morpho Association
/// @custom:contact security@morpho.org
/// @dev Extension of ERC20 to Optimism network deployment.
///
/// This extension allows the StandardBridge contracts to mint and burn tokens. This makes it possible to use an
/// OptimismMintablERC20 as the L2 representation of an L1 token, or vice-versa. Designed to be backwards compatible
/// with the older StandardL2ERC20 token which was only meant for use on L2.
contract OptimismMintableERC20Upgradeable is
Initializable,
IOptimismMintableERC20,
ILegacyMintableERC20,
ERC20Upgradeable
{
contract OptimismMintableERC20Upgradeable is Initializable, IOptimismMintableERC20, ERC20Upgradeable {
/* CONSTANTS */

// keccak256(abi.encode(uint256(keccak256("morpho.storage.OptimismMintableERC20")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OptimismMintableERC20StorageLocation =
0x6fd4c0a11d0843c68c809f0a5f29b102d54bc08a251c384d9ad17600bfa05d00;

/* STRUCTS */

/// @custom:storage-location erc7201:morpho.storage.ERC20Delegates
/// @custom:storage-location erc7201:morpho.storage.OptimismMintableERC20
struct OptimismMintableERC20Storage {
address _REMOTE_TOKEN;
address _BRIDGE;
address _remoteToken;
address _bridge;
}

///@dev Emitted whenever tokens are minted for an account.
/* EVENTS */

/// @dev Emitted whenever tokens are minted for an account.
event Mint(address indexed account, uint256 amount);

///@dev Emitted whenever tokens are burned from an account.
/// @dev Emitted whenever tokens are burned from an account.
event Burn(address indexed account, uint256 amount);

///@dev A modifier that only allows the bridge to call
/* ERRORS */

/// @notice Reverts if the address is the zero address.
error ZeroAddress();

/// @notice Reverts if the caller is not the bridge.
error NotBridge(address caller);

/* MODIFIERS */

/// @dev A modifier that only allows the bridge to call.
modifier onlyBridge() {
OptimismMintableERC20Storage storage $ = _getOptimismMintableERC20Storage();
require(_msgSender() == $._BRIDGE, "OptimismMintableERC20: only bridge can mint and burn");
require(_msgSender() == $._bridge, NotBridge(_msgSender()));
_;
}

///@dev Sets the values for {remoteToken} and {bridge}.
///@dev All two of these values are immutable: they can only be set once during initialization.
/* INITIALIZER */

/// @dev Sets the values for {remoteToken} and {bridge}.
/// @dev All two of these values are immutable: they can only be set once during initialization.
function __OptimismMintableERC20_init(address remoteToken_, address bridge_) internal onlyInitializing {
__OptimismMintableERC20_init_unchained(remoteToken_, bridge_);
}

function __OptimismMintableERC20_init_unchained(address remoteToken_, address bridge_) internal onlyInitializing {
require(remoteToken_ != address(0), ZeroAddress());
require(bridge_ != address(0), ZeroAddress());

OptimismMintableERC20Storage storage $ = _getOptimismMintableERC20Storage();
$._REMOTE_TOKEN = remoteToken_;
$._BRIDGE = bridge_;
$._remoteToken = remoteToken_;
$._bridge = bridge_;
}

///@dev Allows the StandardBridge on this network to mint tokens.
function mint(address _to, uint256 _amount)
external
virtual
override(IOptimismMintableERC20, ILegacyMintableERC20)
onlyBridge
{
_mint(_to, _amount);
emit Mint(_to, _amount);
/* EXTERNAL */

/// @dev Allows the StandardBridge on this network to mint tokens.
function mint(address to, uint256 amount) external virtual override onlyBridge {
_mint(to, amount);
emit Mint(to, amount);
}

///@dev Allows the StandardBridge on this network to burn tokens.
function burn(address _from, uint256 _amount)
external
virtual
override(IOptimismMintableERC20, ILegacyMintableERC20)
onlyBridge
{
_burn(_from, _amount);
emit Burn(_from, _amount);
/// @dev Allows the StandardBridge on this network to burn tokens.
function burn(address from, uint256 amount) external virtual override onlyBridge {
_burn(from, amount);
emit Burn(from, amount);
}

/**
* @notice ERC165 interface check function.
*
* @param _interfaceId Interface ID to check.
*
* @return Whether or not the interface is supported by this contract.
*/
/// @notice ERC165 interface check function.
/// @param _interfaceId Interface ID to check.
/// @return Whether or not the interface is supported by this contract.
function supportsInterface(bytes4 _interfaceId) external pure returns (bool) {
bytes4 iface1 = type(IERC165).interfaceId;
// Interface corresponding to the legacy L2StandardERC20.
bytes4 iface2 = type(ILegacyMintableERC20).interfaceId;
// Interface corresponding to the updated OptimismMintableERC20 (this contract).
bytes4 iface3 = type(IOptimismMintableERC20).interfaceId;
return _interfaceId == iface1 || _interfaceId == iface2 || _interfaceId == iface3;
}

/// @dev Legacy getter for the remote token. Use REMOTE_TOKEN going forward.
function l1Token() public view returns (address) {
OptimismMintableERC20Storage storage $ = _getOptimismMintableERC20Storage();
return $._BRIDGE;
return _interfaceId == iface1 || _interfaceId == iface3;
}

///@dev Legacy getter for the bridge. Use BRIDGE going forward.
function l2Bridge() public view returns (address) {
OptimismMintableERC20Storage storage $ = _getOptimismMintableERC20Storage();
return $._BRIDGE;
}
/* PUBLIC */

///@dev Legacy getter for REMOTE_TOKEN.
/// @custom:legacy
/// @dev Legacy getter for REMOTE_TOKEN.
function remoteToken() public view returns (address) {
OptimismMintableERC20Storage storage $ = _getOptimismMintableERC20Storage();
return $._REMOTE_TOKEN;
return $._remoteToken;
}

///@dev Legacy getter for BRIDGE.
/// @custom:legacy
/// @dev Legacy getter for BRIDGE.
function bridge() public view returns (address) {
OptimismMintableERC20Storage storage $ = _getOptimismMintableERC20Storage();
return $._BRIDGE;
return $._bridge;
}

/* PRIVATE */

/// @dev Returns the OptimismMintableERC20Storage struct.
function _getOptimismMintableERC20Storage() private pure returns (OptimismMintableERC20Storage storage $) {
assembly {
Expand Down
2 changes: 1 addition & 1 deletion src/Wrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.27;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @title MorphoToken
/// @author Morpho Labs
/// @author Morpho Association
/// @custom:contact security@morpho.org
/// @notice The MORPHO Token contract.
contract Wrapper {
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IDelegates.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.20;

/// @title IDelegates
/// @author Morpho Labs
/// @author Morpho Association
/// @custom:contact security@morpho.org
/// @notice The Delegates interface.
interface IDelegates {
Expand Down
17 changes: 2 additions & 15 deletions src/interfaces/IOptimismMintableERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,7 @@ interface IOptimismMintableERC20 is IERC165 {

function bridge() external returns (address);

function mint(address _to, uint256 _amount) external;
function mint(address to, uint256 amount) external;

function burn(address _from, uint256 _amount) external;
}

/// @custom:legacy
/// @title ILegacyMintableERC20
/// @notice This interface was available on the legacy L2StandardERC20 contract.
/// It remains available on the OptimismMintableERC20 contract for
/// backwards compatibility.
interface ILegacyMintableERC20 is IERC165 {
function l1Token() external view returns (address);

function mint(address _to, uint256 _amount) external;

function burn(address _from, uint256 _amount) external;
function burn(address from, uint256 amount) external;
}
2 changes: 1 addition & 1 deletion test/helpers/interfaces/IMulticall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity >=0.5.0;

/// @title IMulticall
/// @author Morpho Labs
/// @author Morpho Association
/// @custom:contact security@morpho.org
/// @notice Interface of Multicall.
interface IMulticall {
Expand Down

0 comments on commit c02a6cc

Please sign in to comment.