diff --git a/contracts/legacy/optimism/IOptimismMintableERC20.sol b/contracts/legacy/optimism/IOptimismMintableERC20.sol new file mode 100644 index 0000000..81a8e19 --- /dev/null +++ b/contracts/legacy/optimism/IOptimismMintableERC20.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; + +/** + * @title IOptimismMintableERC20 + * @notice This interface is available on the OptimismMintableERC20 contract. We declare it as a + * separate interface so that it can be used in custom implementations of + * OptimismMintableERC20. + */ +interface IOptimismMintableERC20 is IERC165 { + function remoteToken() external view returns (address); + + function bridge() external returns (address); + + 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; +} diff --git a/contracts/legacy/optimism/OptimismMintableMiniMeToken.sol b/contracts/legacy/optimism/OptimismMintableMiniMeToken.sol new file mode 100644 index 0000000..c2e0c5b --- /dev/null +++ b/contracts/legacy/optimism/OptimismMintableMiniMeToken.sol @@ -0,0 +1,119 @@ + + +// SPDX-License-Identifier: MIT +pragma solidity 0.8.18; + +import { MiniMeToken } from "../token/MiniMeToken.sol"; +import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { ILegacyMintableERC20, IOptimismMintableERC20 } from "./IOptimismMintableERC20.sol"; +import { Semver } from "./Semver.sol"; + +/// @title OptimismMintableMiniMeToken +/// @notice OptimismMintableMiniMeToken is a standard extension of the base MiniMeToken token contract designed +/// to allow 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 OptimismMintableMiniMeToken is IOptimismMintableERC20, ILegacyMintableERC20, MiniMeToken, Semver { + /// @notice Address of the corresponding version of this token on the remote chain. + address public immutable REMOTE_TOKEN; + + /// @notice Address of the StandardBridge on this network. + address public immutable BRIDGE; + + /// @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. + event Mint(address indexed account, uint256 amount); + + /// @notice Emitted whenever tokens are burned from an account. + /// @param account Address of the account tokens are being burned from. + /// @param amount Amount of tokens burned. + event Burn(address indexed account, uint256 amount); + + /// @notice A modifier that only allows the bridge to call + modifier onlyBridge() { + require(msg.sender == BRIDGE, "OptimismMintableMiniMeToken: only bridge can mint and burn"); + _; + } + + /// @custom:semver 1.0.1 + /// @param _bridge Address of the L2 standard bridge. + /// @param _remoteToken Address of the corresponding L1 token. + /// @param _tokenName ERC20 name. + /// @param _tokenSymbol ERC20 symbol. + constructor( + address _bridge, + address _remoteToken, + address _tokenFactory, + address payable _parentToken, + uint _parentSnapShotBlock, + string memory _tokenName, + uint8 _decimalUnits, + string memory _tokenSymbol, + bool _transfersEnabled + ) MiniMeToken(_tokenFactory, _parentToken, _parentSnapShotBlock, _tokenName, _decimalUnits, _tokenSymbol, _transfersEnabled) Semver(1, 0, 1) { + REMOTE_TOKEN = _remoteToken; + BRIDGE = _bridge; + } + + /// @notice Allows the StandardBridge on this network to mint tokens. + /// @param _to Address to mint tokens to. + /// @param _amount Amount of tokens to mint. + function mint(address _to, uint256 _amount) + external + override(IOptimismMintableERC20, ILegacyMintableERC20) + onlyBridge + { + _mint(_to, _amount); + emit Mint(_to, _amount); + } + + /// @notice Allows the StandardBridge on this network to burn tokens. + /// @param _from Address to burn tokens from. + /// @param _amount Amount of tokens to burn. + function burn(address _from, uint256 _amount) + external + override(IOptimismMintableERC20, ILegacyMintableERC20) + 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. + 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 OptimismMintableMiniMeToken (this contract). + bytes4 iface3 = type(IOptimismMintableERC20).interfaceId; + return _interfaceId == iface1 || _interfaceId == iface2 || _interfaceId == iface3; + } + + /// @custom:legacy + /// @notice Legacy getter for the remote token. Use REMOTE_TOKEN going forward. + function l1Token() public view returns (address) { + return REMOTE_TOKEN; + } + + /// @custom:legacy + /// @notice Legacy getter for the bridge. Use BRIDGE going forward. + function l2Bridge() public view returns (address) { + return BRIDGE; + } + + /// @custom:legacy + /// @notice Legacy getter for REMOTE_TOKEN. + function remoteToken() public view returns (address) { + return REMOTE_TOKEN; + } + + /// @custom:legacy + /// @notice Legacy getter for BRIDGE. + function bridge() public view returns (address) { + return BRIDGE; + } +} \ No newline at end of file diff --git a/contracts/legacy/optimism/Semver.sol b/contracts/legacy/optimism/Semver.sol new file mode 100644 index 0000000..57d6c6c --- /dev/null +++ b/contracts/legacy/optimism/Semver.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; + +/// @title Semver +/// @notice Semver is a simple contract for managing contract versions. +contract Semver { + /// @notice Contract version number (major). + uint256 private immutable MAJOR_VERSION; + + /// @notice Contract version number (minor). + uint256 private immutable MINOR_VERSION; + + /// @notice Contract version number (patch). + uint256 private immutable PATCH_VERSION; + + /// @param _major Version number (major). + /// @param _minor Version number (minor). + /// @param _patch Version number (patch). + constructor( + uint256 _major, + uint256 _minor, + uint256 _patch + ) { + MAJOR_VERSION = _major; + MINOR_VERSION = _minor; + PATCH_VERSION = _patch; + } + + /// @notice Returns the full semver contract version. + /// @return Semver contract version as a string. + function version() public view returns (string memory) { + return + string( + abi.encodePacked( + Strings.toString(MAJOR_VERSION), + ".", + Strings.toString(MINOR_VERSION), + ".", + Strings.toString(PATCH_VERSION) + ) + ); + } +} \ No newline at end of file diff --git a/contracts/legacy/token/MiniMeToken.sol b/contracts/legacy/token/MiniMeToken.sol index 3eba4fb..70cd1b9 100644 --- a/contracts/legacy/token/MiniMeToken.sol +++ b/contracts/legacy/token/MiniMeToken.sol @@ -41,7 +41,7 @@ contract MiniMeToken is Controlled { string public name; //The Token's name: e.g. DigixDAO Tokens uint8 public decimals; //Number of decimals of the smallest unit string public symbol; //An identifier: e.g. REP - string public version = "MMT_0.1"; //An arbitrary versioning scheme + string public token_version = "MMT_0.1"; //An arbitrary versioning scheme /** * @dev `Checkpoint` is the structure that attaches a block number to a @@ -254,6 +254,36 @@ contract MiniMeToken is Controlled { return true; } + function _mint( + address _owner, + uint _amount + ) + internal + { + uint curTotalSupply = totalSupplyAt(block.number); + require(curTotalSupply + _amount >= curTotalSupply, "Total overflow"); // Check for overflow + uint previousBalanceTo = balanceOfAt(_owner, block.number); + require(previousBalanceTo + _amount >= previousBalanceTo, "Balance overflow"); // Check for overflow + updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); + updateValueAtNow(balances[_owner], previousBalanceTo + _amount); + emit Transfer(address(0), _owner, _amount); + } + + function _burn( + address _owner, + uint _amount + ) + internal + { + uint curTotalSupply = totalSupplyAt(block.number); + require(curTotalSupply >= _amount, "No enough supply"); + uint previousBalanceFrom = balanceOfAt(_owner, block.number); + require(previousBalanceFrom >= _amount, "No enough balance"); + updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount); + updateValueAtNow(balances[_owner], previousBalanceFrom - _amount); + emit Transfer(_owner, address(0), _amount); + } + /** * @param _owner The address that's balance is being requested * @return balance The balance of `_owner` at the current block @@ -456,13 +486,7 @@ contract MiniMeToken is Controlled { onlyController returns (bool) { - uint curTotalSupply = totalSupplyAt(block.number); - require(curTotalSupply + _amount >= curTotalSupply, "Total overflow"); // Check for overflow - uint previousBalanceTo = balanceOfAt(_owner, block.number); - require(previousBalanceTo + _amount >= previousBalanceTo, "Balance overflow"); // Check for overflow - updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount); - updateValueAtNow(balances[_owner], previousBalanceTo + _amount); - emit Transfer(address(0), _owner, _amount); + _mint(_owner, _amount); return true; } @@ -480,13 +504,7 @@ contract MiniMeToken is Controlled { onlyController returns (bool) { - uint curTotalSupply = totalSupplyAt(block.number); - require(curTotalSupply >= _amount, "No enough supply"); - uint previousBalanceFrom = balanceOfAt(_owner, block.number); - require(previousBalanceFrom >= _amount, "No enough balance"); - updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount); - updateValueAtNow(balances[_owner], previousBalanceFrom - _amount); - emit Transfer(_owner, address(0), _amount); + _burn(_owner, _amount); return true; } diff --git a/hardhat.config.js b/hardhat.config.js index 868ac12..383a7b0 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -5,12 +5,21 @@ const INFURA_API_KEY = process.env.INFURA_API_KEY; const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY; const GOERLI_PRIVATE_KEY = process.env.GOERLI_PRIVATE_KEY; -console.log(INFURA_API_KEY) /** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.8.18", networks: { - goerli: { + 'optimism-goerli': { + chainId: 420, + url: `https://opt-goerli.g.alchemy.com/v2/${process.env.L2_ALCHEMY_KEY}`, + accounts: { mnemonic: process.env.MNEMONIC } + }, + 'optimism-mainnet': { + chainId: 10, + url: `https://opt-mainnet.g.alchemy.com/v2/${process.env.L2_ALCHEMY_KEY}`, + accounts: { mnemonic: process.env.MNEMONIC } + }, + 'goerli': { url: `https://goerli.infura.io/v3/${INFURA_API_KEY}`, accounts: [GOERLI_PRIVATE_KEY] }