pragma solidity ^0.5.16; pragma experimental ABIEncoderV2; // Inheritance import "./BaseOikosBridge.sol"; import "./interfaces/IOikosBridgeToBase.sol"; import "@eth-optimism/contracts/iOVM/bridge/tokens/iOVM_L2DepositedToken.sol"; // Internal references import "@eth-optimism/contracts/iOVM/bridge/tokens/iOVM_L1TokenGateway.sol"; contract OikosBridgeToBase is BaseOikosBridge, IOikosBridgeToBase, iOVM_L2DepositedToken { /* ========== ADDRESS RESOLVER CONFIGURATION ========== */ bytes32 private constant CONTRACT_BASE_SYNTHETIXBRIDGETOOPTIMISM = "base:OikosBridgeToOptimism"; // ========== CONSTRUCTOR ========== constructor(address _owner, address _resolver) public BaseOikosBridge(_owner, _resolver) {} // ========== INTERNALS ============ function oikosBridgeToOptimism() internal view returns (address) { return requireAndGetAddress(CONTRACT_BASE_SYNTHETIXBRIDGETOOPTIMISM); } function onlyAllowFromOptimism() internal view { // ensure function only callable from the L2 bridge via messenger (aka relayer) iAbs_BaseCrossDomainMessenger _messenger = messenger(); require(msg.sender == address(_messenger), "Only the relayer can call this"); require(_messenger.xDomainMessageSender() == oikosBridgeToOptimism(), "Only the L1 bridge can invoke"); } modifier onlyOptimismBridge() { onlyAllowFromOptimism(); _; } // ========== VIEWS ========== function resolverAddressesRequired() public view returns (bytes32[] memory addresses) { bytes32[] memory existingAddresses = BaseOikosBridge.resolverAddressesRequired(); bytes32[] memory newAddresses = new bytes32[](1); newAddresses[0] = CONTRACT_BASE_SYNTHETIXBRIDGETOOPTIMISM; addresses = combineArrays(existingAddresses, newAddresses); } // ========== PUBLIC FUNCTIONS ========= // invoked by user on L2 function withdraw(uint amount) external requireInitiationActive { _initiateWithdraw(msg.sender, amount); } function withdrawTo(address to, uint amount) external requireInitiationActive { _initiateWithdraw(to, amount); } function _initiateWithdraw(address to, uint amount) private { require(oikos().transferableOikos(msg.sender) >= amount, "Not enough transferable OKS"); // instruct L2 Oikos to burn this supply oikos().burnSecondary(msg.sender, amount); // create message payload for L1 iOVM_L1TokenGateway bridgeToOptimism; bytes memory messageData = abi.encodeWithSelector(bridgeToOptimism.finalizeWithdrawal.selector, to, amount); // relay the message to Bridge on L1 via L2 Messenger messenger().sendMessage( oikosBridgeToOptimism(), messageData, uint32(getCrossDomainMessageGasLimit(CrossDomainMessageGasLimits.Withdrawal)) ); emit iOVM_L2DepositedToken.WithdrawalInitiated(msg.sender, to, amount); } // ========= RESTRICTED FUNCTIONS ============== function finalizeEscrowMigration( address account, uint256 escrowedAmount, VestingEntries.VestingEntry[] calldata vestingEntries ) external onlyOptimismBridge { IRewardEscrowV2 rewardEscrow = rewardEscrowV2(); // First, mint the escrowed OKS that are being migrated oikos().mintSecondary(address(rewardEscrow), escrowedAmount); rewardEscrow.importVestingEntries(account, escrowedAmount, vestingEntries); emit ImportedVestingEntries(account, escrowedAmount, vestingEntries); } // invoked by Messenger on L2 function finalizeDeposit(address to, uint256 amount) external onlyOptimismBridge { // now tell Oikos to mint these tokens, deposited in L1, into the specified account for L2 oikos().mintSecondary(to, amount); emit iOVM_L2DepositedToken.DepositFinalized(to, amount); } // invoked by Messenger on L2 function finalizeRewardDeposit(address from, uint256 amount) external onlyOptimismBridge { // now tell Oikos to mint these tokens, deposited in L1, into reward escrow on L2 oikos().mintSecondaryRewards(amount); emit RewardDepositFinalized(from, amount); } // invoked by Messenger on L2 function finalizeFeePeriodClose(uint256 oksBackedAmount, uint256 totalDebtShares) external onlyOptimismBridge { // now tell Oikos to mint these tokens, deposited in L1, into reward escrow on L2 feePool().closeSecondary(oksBackedAmount, totalDebtShares); emit FeePeriodCloseFinalized(oksBackedAmount, totalDebtShares); } // ========== EVENTS ========== event ImportedVestingEntries( address indexed account, uint256 escrowedAmount, VestingEntries.VestingEntry[] vestingEntries ); event RewardDepositFinalized(address from, uint256 amount); event FeePeriodCloseFinalized(uint oksBackedAmount, uint totalDebtShares); }