Skip to content
This repository has been archived by the owner on Apr 12, 2021. It is now read-only.

Commit

Permalink
Create Unibridge for bridging any token to Optimism
Browse files Browse the repository at this point in the history
  • Loading branch information
dmihal committed Mar 3, 2021
1 parent 3546140 commit d8c2f30
Show file tree
Hide file tree
Showing 17 changed files with 2,223 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
// SPDX-License-Identifier: MIT
// @unsupported: ovm
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;

/* Interface Imports */
import { iOVM_L1TokenBridge } from "../../../iOVM/bridge/unibridge/iOVM_L1TokenBridge.sol";
import { iOVM_L2TokenBridge } from "../../../iOVM/bridge/unibridge/iOVM_L2TokenBridge.sol";
import { iOVM_ERC20 } from "../../../iOVM/precompiles/iOVM_ERC20.sol";

/* Library Imports */
import { OVM_CrossDomainEnabled } from "../../../libraries/bridge/OVM_CrossDomainEnabled.sol";
import { UniSafeERC20Namer } from "../../../libraries/standards/UniSafeERC20Namer.sol";

/**
* @title OVM_L1TokenBridge
* @dev The L1 Token Bridge is a contract which stores deposited L1 funds that are in use on L2.
* It synchronizes a corresponding L2 ERC20 Bridge, informing it of deposits, and listening to it
* for newly finalized withdrawals.
*
* Compiler used: solc
* Runtime target: EVM
*/
contract OVM_L1TokenBridge is iOVM_L1TokenBridge, OVM_CrossDomainEnabled {
/********************************
* External Contract References *
********************************/

address public immutable l2Bridge;
bytes32 public immutable l2ERC20BytecodeHash;
bytes32 public immutable l2ERC777BytecodeHash;

/***************
* Constructor *
***************/

/**
* @param _l2Bridge L2 bridge contract address
* @param _l1messenger L1 Messenger address being used for cross-chain communications.
* @param _l2ERC20BytecodeHash Hash of the L2 ERC20 contract bytecode, used to calculate token addresses
* @param _l2ERC777BytecodeHash Hash of the L2 ERC777 contract bytecode, used to calculate token addresses
*/
constructor(
address _l2Bridge,
address _l1messenger,
bytes32 _l2ERC20BytecodeHash,
bytes32 _l2ERC777BytecodeHash
)
OVM_CrossDomainEnabled(_l1messenger)
{
l2Bridge = _l2Bridge;
l2ERC20BytecodeHash = _l2ERC20BytecodeHash;
l2ERC777BytecodeHash = _l2ERC777BytecodeHash;
}

/**********************
* L2 Token Addresses *
**********************/

/**
* Calculates the addres of a bridged ERC777 on L2
* @param _l1Token The ERC20 token on L1
* @return calculatedAddress The address of the bridged ERC777 on L2
*/
function calculateL2ERC777Address(address _l1Token) public view returns (address calculatedAddress) {
calculatedAddress = address(uint(keccak256(abi.encodePacked(
byte(0xff),
l2Bridge,
bytes32(uint(_l1Token)),
l2ERC777BytecodeHash
))));
}

/**
* Calculates the addres of a bridged ERC20 on L2
* @param _l1Token The ERC20 token on L1
* @return calculatedAddress The address of the bridged ERC20 on L2
*/
function calculateL2ERC20Address(address _l1Token) public view returns (address calculatedAddress) {
calculatedAddress = address(uint(keccak256(abi.encodePacked(
byte(0xff),
l2Bridge,
bytes32(uint(_l1Token)),
l2ERC20BytecodeHash
))));
}

/**************
* Depositing *
**************/

/**
* @dev deposit an amount of ERC20 to a recipients's balance on L2
* @param _to L2 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to deposit
*/
function depositAsERC20(
address token,
address _to,
uint _amount
)
external
override
{
_initiateDeposit(iOVM_L2TokenBridge.depositAsERC20.selector, token, msg.sender, _to, _amount);
}

/**
* @dev deposit an amount of ERC20 to a recipients's balance on L2
* @param _to L2 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to deposit
*/
function depositAsERC777(
address _token,
address _to,
uint _amount
)
external
override
{
require(iOVM_ERC20(_token).decimals() <= 18, "Only ");
_initiateDeposit(iOVM_L2TokenBridge.depositAsERC777.selector, _token, msg.sender, _to, _amount);
}

/**
* @dev Performs the logic for deposits by storing the ERC20 and informing the L2 Deposited ERC20 contract of the deposit.
*
* @param _from Account to pull the deposit from on L1
* @param _to Account to give the deposit to on L2
* @param _amount Amount of the ERC20 to deposit.
*/
function _initiateDeposit(
bytes4 _selector,
address _token,
address _from,
address _to,
uint _amount
)
internal
{
// Hold on to the newly deposited funds
iOVM_ERC20(_token).transferFrom(
_from,
address(this),
_amount
);

uint8 _decimals = iOVM_ERC20(_token).decimals();

// Construct calldata for l2Bridge.finalizeDeposit(_to, _amount)
bytes memory data = abi.encodeWithSelector(_selector, _token, _to, _amount, _decimals);

// Send calldata into L2
sendCrossDomainMessage(
l2Bridge,
data,
DEFAULT_FINALIZE_DEPOSIT_L2_GAS
);

emit DepositInitiated(_token, _from, _to, _amount);
}

/**
* @dev L2 tokens have no name or symbol by default. This function passes that data to L2.
* @param _l1Token Address of the L1 token
*/
function updateTokenInfo(address _l1Token) external {
bytes memory data = abi.encodeWithSelector(
iOVM_L2TokenBridge.updateTokenInfo.selector,
_l1Token,
UniSafeERC20Namer.tokenName(_l1Token),
UniSafeERC20Namer.tokenSymbol(_l1Token)
);
sendCrossDomainMessage(l2Bridge, data, DEFAULT_FINALIZE_DEPOSIT_L2_GAS);
}

/*************************************
* Cross-chain Function: Withdrawing *
*************************************/

/**
* @dev Complete a withdrawal from L2 to L1, and credit funds to the recipient's balance of the
* L1 ERC20 token.
* This call will fail if the initialized withdrawal from L2 has not been finalized.
*
* @param _to L1 address to credit the withdrawal to
* @param _amount Amount of the ERC20 to withdraw
*/
function finalizeWithdrawal(
address _token,
address _to,
uint _amount
)
external
override
onlyFromCrossDomainAccount(l2Bridge)
{
iOVM_ERC20(_token).transfer(_to, _amount);

emit WithdrawalFinalized(_token, _to, _amount);
}
}
106 changes: 106 additions & 0 deletions contracts/optimistic-ethereum/OVM/bridge/unibridge/OVM_L2ERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: MIT
pragma solidity >0.5.0 <0.8.0;
pragma experimental ABIEncoderV2;

/* Interface Imports */
import { iOVM_L2TokenBridge } from "../../../iOVM/bridge/unibridge/iOVM_L2TokenBridge.sol";

/* Contract Imports */
import { UniswapV2ERC20 } from "../../../libraries/standards/UniswapV2ERC20.sol";

/**
* @title OVM_L2ERC20
* @dev The L2 Deposited ERC20 is an ERC20 implementation which represents L1 assets deposited into L2.
* This contract mints new tokens when the token bridge receives deposit messages.
* This contract also burns the tokens intended for withdrawal and calls the bridge contract.
* The name & symbol will be empty by default, and can be set by calling updateTokenInfo on the L1 bridge.
*
* Compiler used: optimistic-solc
* Runtime target: OVM
*/
contract OVM_L2ERC20 is UniswapV2ERC20 {
/********************************
* External Contract References *
********************************/

address public immutable bridge;
address public l1Address;

/********************************
* Constructor & Initialization *
********************************/

constructor() public UniswapV2ERC20(0, "", "") {
bridge = msg.sender;
}

/**
* @dev Initialize the contract immediately after construction, passing in the
* L1 token address and the number of decimals.
*
* @param _l1Address Address of the corresponding token on L1
* @param _decimals Number of decimal places of the token
*/
function initialize(address _l1Address, uint8 _decimals) external onlyBridge {
l1Address = _l1Address;
decimals = _decimals;
}

/**********************
* Function Modifiers *
**********************/

modifier onlyBridge {
require(msg.sender == bridge, "May only be called by the bridge");
_;
}

/******************
* User Functions *
******************/

/**
* @dev Initiate a withdraw of some ERC20 to a recipient's account on L1
* @param _destination L1 adress to credit the withdrawal to
* @param _amount Amount of the ERC20 to withdraw
*/
function withdraw(address _destination, uint256 _amount) external {
_burn(msg.sender, _amount);
iOVM_L2TokenBridge(bridge).withdraw(l1Address, _destination, _amount);
}

/**
* @dev Migrate tokens from ERC20 to ERC777
* @param _amount Amount of the ERC20 to migrate
* @param _target The address of the ERC777 token
*/
function migrate(uint256 _amount, address _target) external {
_burn(msg.sender, _amount);
iOVM_L2TokenBridge(bridge).migrate(l1Address, _target, msg.sender, _amount);
}

/********************
* Bridge functions *
********************/

/**
* @dev Receives the name & symbol of the token from the bridge.
*
* @param _newName The token's name
* @param _newSymbol The token's symbol
*/
function updateInfo(string memory _newName, string memory _newSymbol) external onlyBridge {
name = _newName;
symbol = _newSymbol;
}

/**
* @dev Mints new tokens to a user.
*
* @param _recipient The address to receive the tokens.
* @param _amount The amount of tokens to mint.
*/
function mint(address _recipient, uint256 _amount) external onlyBridge {
_mint(_recipient, _amount);
}
}
Loading

0 comments on commit d8c2f30

Please sign in to comment.