// SPDX-License-Identifier: MIT // +build ovm pragma solidity >0.5.0 <0.8.0; pragma experimental ABIEncoderV2; /* Library Imports */ import { Lib_AddressResolver } from "@eth-optimism/contracts/build/contracts/libraries/resolver/Lib_AddressResolver.sol"; import { Lib_ReentrancyGuard } from "@eth-optimism/contracts/build/contracts/libraries/utils/Lib_ReentrancyGuard.sol"; /* Interface Imports */ import { iL2_CustomCrossDomainMessenger } from "./interfaces/iL2_CustomCrossDomainMessenger.sol"; import { iOVM_L1MessageSender } from "@eth-optimism/contracts/build/contracts/iOVM/precompiles/iOVM_L1MessageSender.sol"; import { iOVM_L2ToL1MessagePasser } from "@eth-optimism/contracts/build/contracts/iOVM/precompiles/iOVM_L2ToL1MessagePasser.sol"; /* Contract Imports */ import { BaseCustomCrossDomainMessenger } from "./BaseCustomCrossDomainMessenger.sol"; contract L2_CustomCrossDomainMessenger is iL2_CustomCrossDomainMessenger, BaseCustomCrossDomainMessenger, Lib_AddressResolver { address l1_customCrossDomainMessenger; /*************** * Constructor * ***************/ /** * @param _libAddressManager Address of the Address Manager. */ constructor(address _libAddressManager) Lib_AddressResolver(_libAddressManager) {} /** * @param _l1_customCrossDomainMessenger Address of the L1_CustomCrossDomainMessenger contract this contract depends on. */ function initialize(address _l1_customCrossDomainMessenger) public { require( address(l1_customCrossDomainMessenger) == address(0), "L2_CustomCrossDomainMessenger already intialized." ); l1_customCrossDomainMessenger = _l1_customCrossDomainMessenger; } /******************** * Public Functions * ********************/ /** * Relays a cross domain message to a contract. * @inheritdoc iL2_CustomCrossDomainMessenger */ function relayMessage( address _target, address _sender, bytes memory _message, uint256 _messageNonce, uint256 _timelock ) public override nonReentrant { require( _verifyXDomainMessage() == true, "Provided message could not be verified." ); bytes memory xDomainCalldata = _getXDomainCalldata( _target, _sender, _message, _messageNonce, _timelock ); bytes32 xDomainCalldataHash = keccak256(xDomainCalldata); require( successfulMessages[xDomainCalldataHash] == false, "Provided message has already been received." ); xDomainMessageSender = _sender; (bool success, ) = _target.call(_message); // Mark the message as received if the call was successful. Ensures that a message can be // relayed multiple times in the case that the call reverted. if (success == true) { successfulMessages[xDomainCalldataHash] = true; emit RelayedMessage(xDomainCalldataHash); } // Store an identifier that can be used to prove that the given message was relayed by some // user. Gives us an easy way to pay relayers for their work. bytes32 relayId = keccak256( abi.encodePacked(xDomainCalldata, msg.sender, block.number) ); relayedMessages[relayId] = true; } /********************** * Internal Functions * **********************/ /** * Verifies that a received cross domain message is valid. * @return _valid Whether or not the message is valid. */ function _verifyXDomainMessage() internal returns (bool _valid) { return (iOVM_L1MessageSender(resolve("OVM_L1MessageSender")) .getL1MessageSender() == l1_customCrossDomainMessenger); } /** * Sends a cross domain message. * @param _message Message to send. * @param _gasLimit Gas limit for the provided message. */ function _sendXDomainMessage(bytes memory _message, uint256 _gasLimit) internal override { iOVM_L2ToL1MessagePasser(resolve("OVM_L2ToL1MessagePasser")) .passMessageToL1(_message); } }