From d5ea6668c3e477efb31b65c6b2446cd23801d9d1 Mon Sep 17 00:00:00 2001 From: Kristijan Rebernisak Date: Mon, 28 Jun 2021 17:11:42 +0200 Subject: [PATCH] Add LinkToken on Optimism --- CHANGELOG.md | 3 +- contracts/v0.7/bridge/README.md | 2 + .../bridge/token/optimism/IERC20Optimism.sol | 50 ++++++++ .../token/optimism/LinkTokenOptimism.sol | 109 ++++++++++++++++++ .../v0.7/bridge/token/optimism/README.md | 13 +++ 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 contracts/v0.7/bridge/token/optimism/IERC20Optimism.sol create mode 100644 contracts/v0.7/bridge/token/optimism/LinkTokenOptimism.sol create mode 100644 contracts/v0.7/bridge/token/optimism/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 16f1de6..40b01c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - Apr 2021 +## [Unreleased] - Jun 2021 ### Added - Access controlled mintable & burnable LinkToken, for use on sidechains and L2 networks. +- Documentation and token implementation for the Optimism bridge v2. - Added versioning to v0.6 & v0.7 contracts ### Changed diff --git a/contracts/v0.7/bridge/README.md b/contracts/v0.7/bridge/README.md index d347f96..e39163d 100644 --- a/contracts/v0.7/bridge/README.md +++ b/contracts/v0.7/bridge/README.md @@ -23,3 +23,5 @@ NOTICE: Current implementation of LinkTokenChild contract requires some addition gets defined, and once online transfer the ownership (bridge gateway role) to the new bridge. TODO: Potentially create an upgradeable `LinkTokenChild.sol` and limit gateway support to only one (owner)! + +- `./token/optimism/`: Documentation and token implementation for the Optimism bridge v2. diff --git a/contracts/v0.7/bridge/token/optimism/IERC20Optimism.sol b/contracts/v0.7/bridge/token/optimism/IERC20Optimism.sol new file mode 100644 index 0000000..2b15021 --- /dev/null +++ b/contracts/v0.7/bridge/token/optimism/IERC20Optimism.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.6.0 <0.8.0; + +/* Interface Imports */ +import { IERC20 } from "../../../../../vendor/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; +import { IERC165 } from "../../../../../vendor/OpenZeppelin/openzeppelin-contracts/contracts/introspection/IERC165.sol"; + +/// @dev Interface for the bridged ERC20 token expected by the Optimism standard bridge L2 gateway. +interface IERC20Optimism is IERC20, IERC165 { + /// @dev Returns the address of an L1 token contract linked to this L2 token contract + function l1Token() + external + returns (address); + + /** + * @dev Creates `_amount` tokens `_to` account. + * @notice Called by L2 gateway to deposit tokens. + * @param _to Address of the recipient. + * @param _amount Number of tokens to mint. + */ + function mint( + address _to, + uint256 _amount + ) + external; + + /** + * @dev Destroys `_amount` tokens `_from` account. + * @notice Called by L2 gateway to withdraw tokens. + * @param _from Address of the account holding the tokens to be burnt. + * @param _amount Number of tokens to burn. + */ + function burn( + address _from, + uint256 _amount + ) + external; + + /// @dev Emitted when `_amount` tokens are deposited from L1 to L2. + event Mint( + address indexed _account, + uint256 _amount + ); + + /// @dev Emitted when `_amount` tokens are withdrawn from L2 to L1. + event Burn( + address indexed _account, + uint256 _amount + ); +} diff --git a/contracts/v0.7/bridge/token/optimism/LinkTokenOptimism.sol b/contracts/v0.7/bridge/token/optimism/LinkTokenOptimism.sol new file mode 100644 index 0000000..48f9df2 --- /dev/null +++ b/contracts/v0.7/bridge/token/optimism/LinkTokenOptimism.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity >0.6.0 <0.8.0; + +/* Interface Imports */ +import { IERC165 } from "../../../../../vendor/OpenZeppelin/openzeppelin-contracts/contracts/introspection/IERC165.sol"; +import { ITypeAndVersion } from "../../../../v0.6/ITypeAndVersion.sol"; +import { IERC20Optimism } from "./IERC20Optimism.sol"; + +/* Contract Imports */ +import { ERC20 } from "../../../../../vendor/OpenZeppelin/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; +import { LinkToken } from "../../../../v0.6/LinkToken.sol"; + +/// @dev Access controlled mintable & burnable LinkToken, for use on Optimism L2 network. +contract LinkTokenOptimism is ITypeAndVersion, IERC20Optimism, LinkToken { + /// @dev Returns the address of an L2 bridge contract that has access to mint & burn + address public immutable l2Bridge; + /// @inheritdoc IERC20Optimism + address public immutable override l1Token; + + /** + * @dev Creates an L2 token connected to a specific L2 bridge gateway & L1 token + * @param l2BridgeAddr Address of the corresponding L2 bridge gateway. + * @param l1TokenAddr Address of the corresponding L1 token. + */ + constructor( + address l2BridgeAddr, + address l1TokenAddr + ) { + l2Bridge = l2BridgeAddr; + l1Token = l1TokenAddr; + } + + /** + * @notice versions: + * + * - LinkTokenOptimism 0.0.1: initial release + * + * @inheritdoc ITypeAndVersion + */ + function typeAndVersion() + external + pure + override(ITypeAndVersion, LinkToken) + virtual + returns (string memory) + { + return "LinkTokenOptimism 0.0.1"; + } + + /// @dev Checks that message sender is the L2 bridge contract (locked access to mint & burn) + modifier onlyL2Bridge { + require(msg.sender == l2Bridge, "Only L2 Bridge can mint and burn"); + _; + } + + /** + * @dev Optimism standard bridge L2 gateway uses ERC165 to confirm the required interface + * @inheritdoc IERC165 + */ + function supportsInterface( + bytes4 interfaceId + ) + public + override + pure + returns (bool) + { + bytes4 firstSupportedInterface = bytes4(keccak256("supportsInterface(bytes4)")); // ERC165 + bytes4 secondSupportedInterface = IERC20Optimism.l1Token.selector + ^ IERC20Optimism.mint.selector + ^ IERC20Optimism.burn.selector; + return interfaceId == firstSupportedInterface || interfaceId == secondSupportedInterface; + } + + /// @inheritdoc IERC20Optimism + function mint( + address _to, + uint256 _amount + ) + public + override + onlyL2Bridge() + { + _mint(_to, _amount); + emit Mint(_to, _amount); + } + + /// @inheritdoc IERC20Optimism + function burn( + address _from, + uint256 _amount + ) + public + override + onlyL2Bridge() + { + _burn(_from, _amount); + emit Burn(_from, _amount); + } + + /** + * @dev Overrides parent contract so no tokens are minted on deployment. + * @inheritdoc LinkToken + */ + function _onCreate() + internal + override + {} +} diff --git a/contracts/v0.7/bridge/token/optimism/README.md b/contracts/v0.7/bridge/token/optimism/README.md new file mode 100644 index 0000000..7513550 --- /dev/null +++ b/contracts/v0.7/bridge/token/optimism/README.md @@ -0,0 +1,13 @@ +# LINK Token on Optimism + +- `./token/IERC20Optimism.sol`: Interface for the bridged ERC20 token expected by the Optimism standard bridge L2 gateway. +- `./token/LinkTokenOptimism.sol`: Access controlled mintable & burnable LinkToken, for use on Optimism L2 network. + +`LinkTokenOptimism.sol` is a slightly modified version of Optimism's [`L2StandardERC20.sol`](https://github.com/ethereum-optimism/optimism/blob/master/packages/contracts/contracts/optimistic-ethereum/libraries/standards/L2StandardERC20.sol) and will be connected to the [`OVM_L2StandardBridge.sol`](https://github.com/ethereum-optimism/optimism/blob/master/packages/contracts/contracts/optimistic-ethereum/OVM/bridge/tokens/OVM_L2StandardBridge.sol). Modifications include: + +- Contract versioning via `ITypeAndVersion` interface +- ERC677 support by extending the `LinkToken` contract +- Transfers & approves to the contract itself blocked (provided by `LinkToken` contract) +- `l2Bridge` & `l1Token` were changed from storage vars to immutable vars, which provides some gas savings + +The [Optimism Gateway](https://gateway.optimism.io) bridge UI can be used to move the tokens between networks.