Commit a96b2282 authored by Hamdi Allam's avatar Hamdi Allam Committed by GitHub

sent message event and l2tol2cdm relayMessage entrypoint (#11592)

parent d141b53e
......@@ -112,8 +112,8 @@
"sourceCodeHash": "0xd08a2e6514dbd44e16aa312a1b27b2841a9eab5622cbd05a39c30f543fad673c"
},
"src/L2/L2ToL2CrossDomainMessenger.sol": {
"initCodeHash": "0x652e07372d45f0f861aa65b4a73db55871291b875ced02df893a405419de723a",
"sourceCodeHash": "0xc3e73c2d9abf3c7853d2505a83e475d58e96ab5fc5ad7770d04dea5feb9e5717"
"initCodeHash": "0x6f19eb8ff0950156b65cd92872240c0153ac5f3b6f0861d57bf561fdbcacbeac",
"sourceCodeHash": "0xfea53344596d735eff3be945ed1300dc75a6f8b7b2c02c0043af5b0036f5f239"
},
"src/L2/OptimismSuperchainERC20.sol": {
"initCodeHash": "0xe3dbb0851669708901a4c6bb7ad7d55f9896deeec02cbe53ac58d689ff95b88b",
......
......@@ -54,33 +54,40 @@
{
"inputs": [
{
"internalType": "uint256",
"name": "_destination",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_source",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_nonce",
"type": "uint256"
},
{
"internalType": "address",
"name": "_sender",
"type": "address"
},
{
"internalType": "address",
"name": "_target",
"type": "address"
"components": [
{
"internalType": "address",
"name": "origin",
"type": "address"
},
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "logIndex",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
}
],
"internalType": "struct ICrossL2Inbox.Identifier",
"name": "_id",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "_message",
"name": "_sentMessage",
"type": "bytes"
}
],
......@@ -111,7 +118,7 @@
"outputs": [
{
"internalType": "bytes32",
"name": "msgHash_",
"name": "",
"type": "bytes32"
}
],
......@@ -153,6 +160,18 @@
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "source",
"type": "uint256"
},
{
"indexed": true,
"internalType": "uint256",
"name": "messageNonce",
"type": "uint256"
},
{
"indexed": true,
"internalType": "bytes32",
......@@ -166,6 +185,18 @@
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "source",
"type": "uint256"
},
{
"indexed": true,
"internalType": "uint256",
"name": "messageNonce",
"type": "uint256"
},
{
"indexed": true,
"internalType": "bytes32",
......@@ -176,9 +207,51 @@
"name": "RelayedMessage",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "destination",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "target",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "messageNonce",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes",
"name": "message",
"type": "bytes"
}
],
"name": "SentMessage",
"type": "event"
},
{
"inputs": [],
"name": "EventPayloadNotSentMessage",
"type": "error"
},
{
"inputs": [],
"name": "CrossL2InboxOriginNotL2ToL2CrossDomainMessenger",
"name": "IdOriginNotL2ToL2CrossDomainMessenger",
"type": "error"
},
{
......@@ -215,10 +288,5 @@
"inputs": [],
"name": "ReentrantCall",
"type": "error"
},
{
"inputs": [],
"name": "RelayMessageCallerNotCrossL2Inbox",
"type": "error"
}
]
\ No newline at end of file
......@@ -5,6 +5,7 @@ import { Encoding } from "src/libraries/Encoding.sol";
import { Hashing } from "src/libraries/Hashing.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol";
import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol";
import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol";
import { ISemver } from "src/universal/interfaces/ISemver.sol";
import { SafeCall } from "src/libraries/SafeCall.sol";
......@@ -13,14 +14,14 @@ import { TransientReentrancyAware } from "src/libraries/TransientContext.sol";
/// @notice Thrown when a non-written slot in transient storage is attempted to be read from.
error NotEntered();
/// @notice Thrown when attempting to send a message to the chain that the message is being sent from.
error MessageDestinationSameChain();
/// @notice Thrown when attempting to relay a message where payload origin is not L2ToL2CrossDomainMessenger.
error IdOriginNotL2ToL2CrossDomainMessenger();
/// @notice Thrown when attempting to relay a message and the function caller (msg.sender) is not CrossL2Inbox.
error RelayMessageCallerNotCrossL2Inbox();
/// @notice Thrown when the payload provided to the relay is not a SentMessage event.
error EventPayloadNotSentMessage();
/// @notice Thrown when attempting to relay a message where CrossL2Inbox's origin is not L2ToL2CrossDomainMessenger.
error CrossL2InboxOriginNotL2ToL2CrossDomainMessenger();
/// @notice Thrown when attempting to send a message to the chain that the message is being sent from.
error MessageDestinationSameChain();
/// @notice Thrown when attempting to relay a message whose destination chain is not the chain relaying it.
error MessageDestinationNotRelayChain();
......@@ -54,12 +55,17 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra
bytes32 internal constant CROSS_DOMAIN_MESSAGE_SOURCE_SLOT =
0x711dfa3259c842fffc17d6e1f1e0fc5927756133a2345ca56b4cb8178589fee7;
/// @notice Event selector for the SentMessage event. Will be removed in favor of reading
// the `selector` property directly once crytic/slithe/#2566 is fixed.
bytes32 internal constant SENT_MESSAGE_EVENT_SELECTOR =
0x382409ac69001e11931a28435afef442cbfd20d9891907e8fa373ba7d351f320;
/// @notice Current message version identifier.
uint16 public constant messageVersion = uint16(0);
/// @notice Semantic version.
/// @custom:semver 1.0.0-beta.6
string public constant version = "1.0.0-beta.6";
/// @custom:semver 1.0.0-beta.7
string public constant version = "1.0.0-beta.7";
/// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this
/// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again.
......@@ -70,13 +76,27 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra
/// message.
uint240 internal msgNonce;
/// @notice Emitted whenever a message is sent to a destination
/// @param destination Chain ID of the destination chain.
/// @param target Target contract or wallet address.
/// @param messageNonce Nonce associated with the messsage sent
/// @param sender Address initiating this message call
/// @param message Message payload to call target with.
event SentMessage(
uint256 indexed destination, address indexed target, uint256 indexed messageNonce, address sender, bytes message
);
/// @notice Emitted whenever a message is successfully relayed on this chain.
/// @param messageHash Hash of the message that was relayed.
event RelayedMessage(bytes32 indexed messageHash);
/// @param source Chain ID of the source chain.
/// @param messageNonce Nonce associated with the messsage sent
/// @param messageHash Hash of the message that was relayed.
event RelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash);
/// @notice Emitted whenever a message fails to be relayed on this chain.
/// @param messageHash Hash of the message that failed to be relayed.
event FailedRelayedMessage(bytes32 indexed messageHash);
/// @param source Chain ID of the source chain.
/// @param messageNonce Nonce associated with the messsage sent
/// @param messageHash Hash of the message that failed to be relayed.
event FailedRelayedMessage(uint256 indexed source, uint256 indexed messageNonce, bytes32 indexed messageHash);
/// @notice Retrieves the sender of the current cross domain message. If not entered, reverts.
/// @return sender_ Address of the sender of the current cross domain message.
......@@ -100,90 +120,81 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra
/// @param _destination Chain ID of the destination chain.
/// @param _target Target contract or wallet address.
/// @param _message Message payload to call target with.
/// @return msgHash_ The hash of the message being sent, which can be used for tracking whether
/// the message has successfully been relayed.
function sendMessage(
uint256 _destination,
address _target,
bytes calldata _message
)
external
returns (bytes32 msgHash_)
{
/// @return The hash of the message being sent, used to track whether the message has successfully been relayed.
function sendMessage(uint256 _destination, address _target, bytes calldata _message) external returns (bytes32) {
if (_destination == block.chainid) revert MessageDestinationSameChain();
if (_target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger();
(uint256 source, uint256 nonce, address sender) = (block.chainid, messageNonce(), msg.sender);
bytes memory data = abi.encodeCall(
L2ToL2CrossDomainMessenger.relayMessage, (_destination, source, nonce, sender, _target, _message)
);
msgHash_ = Hashing.hashL2toL2CrossDomainMessengerRelayMessage({
uint256 nonce = messageNonce();
emit SentMessage(_destination, _target, nonce, msg.sender, _message);
msgNonce++;
return Hashing.hashL2toL2CrossDomainMessage({
_destination: _destination,
_source: source,
_source: block.chainid,
_nonce: nonce,
_sender: sender,
_sender: msg.sender,
_target: _target,
_message: _message
});
assembly {
log0(add(data, 0x20), mload(data))
}
msgNonce++;
}
/// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only be executed via
/// cross-chain call from the other messenger OR if the message was already received once and is currently
/// being replayed.
/// @param _destination Chain ID of the destination chain.
/// @param _source Chain ID of the source chain.
/// @param _nonce Nonce of the message being relayed.
/// @param _sender Address of the user who sent the message.
/// @param _target Address that the message is targeted at.
/// @param _message Message payload to call target with.
/// @notice Relays a message that was sent by the other L2ToL2CrossDomainMessenger contract. Can only be executed
/// via cross chain call from the other messenger OR if the message was already received once and is
/// currently being replayed.
/// @param _id Identifier of the SentMessage event to be relayed
/// @param _sentMessage Message payload of the `SentMessage` event
function relayMessage(
uint256 _destination,
uint256 _source,
uint256 _nonce,
address _sender,
address _target,
bytes memory _message
ICrossL2Inbox.Identifier calldata _id,
bytes calldata _sentMessage
)
external
payable
nonReentrant
{
if (msg.sender != Predeploys.CROSS_L2_INBOX) revert RelayMessageCallerNotCrossL2Inbox();
if (CrossL2Inbox(Predeploys.CROSS_L2_INBOX).origin() != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) {
revert CrossL2InboxOriginNotL2ToL2CrossDomainMessenger();
}
if (_destination != block.chainid) revert MessageDestinationNotRelayChain();
if (_target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) {
revert MessageTargetL2ToL2CrossDomainMessenger();
// Ensure the log came from the messenger. Since the log origin is the CDM, there isn't a scenario where
// this can be invoked from the CrossL2Inbox as the SentMessage log is not calldata for this function
if (_id.origin != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) {
revert IdOriginNotL2ToL2CrossDomainMessenger();
}
bytes32 messageHash = Hashing.hashL2toL2CrossDomainMessengerRelayMessage({
_destination: _destination,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
// Signal that this is a cross chain call that needs to have the identifier validated
CrossL2Inbox(Predeploys.CROSS_L2_INBOX).validateMessage(_id, keccak256(_sentMessage));
// Decode the payload
(uint256 destination, address target, uint256 nonce, address sender, bytes memory message) =
_decodeSentMessagePayload(_sentMessage);
// Assert invariants on the message
if (destination != block.chainid) revert MessageDestinationNotRelayChain();
if (target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox();
if (target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger();
uint256 source = _id.chainId;
bytes32 messageHash = Hashing.hashL2toL2CrossDomainMessage({
_destination: destination,
_source: source,
_nonce: nonce,
_sender: sender,
_target: target,
_message: message
});
if (successfulMessages[messageHash]) {
revert MessageAlreadyRelayed();
}
_storeMessageMetadata(_source, _sender);
_storeMessageMetadata(source, sender);
bool success = SafeCall.call(_target, msg.value, _message);
bool success = SafeCall.call(target, msg.value, message);
if (success) {
successfulMessages[messageHash] = true;
emit RelayedMessage(messageHash);
emit RelayedMessage(source, nonce, messageHash);
} else {
emit FailedRelayedMessage(messageHash);
emit FailedRelayedMessage(source, nonce, messageHash);
}
_storeMessageMetadata(0, address(0));
......@@ -205,4 +216,20 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra
tstore(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT, _source)
}
}
function _decodeSentMessagePayload(bytes calldata _payload)
internal
pure
returns (uint256 destination_, address target_, uint256 nonce_, address sender_, bytes memory message_)
{
// Validate Selector (also reverts if LOG0 with no topics)
bytes32 selector = abi.decode(_payload[:32], (bytes32));
if (selector != SENT_MESSAGE_EVENT_SELECTOR) revert EventPayloadNotSentMessage();
// Topics
(destination_, target_, nonce_) = abi.decode(_payload[32:128], (uint256, address, uint256));
// Data
(sender_, message_) = abi.decode(_payload[128:], (address, bytes));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol";
/// @title IL2ToL2CrossDomainMessenger
/// @notice Interface for the L2ToL2CrossDomainMessenger contract.
interface IL2ToL2CrossDomainMessenger {
......@@ -45,20 +47,7 @@ interface IL2ToL2CrossDomainMessenger {
/// @notice Relays a message that was sent by the other CrossDomainMessenger contract. Can only
/// be executed via cross-chain call from the other messenger OR if the message was
/// already received once and is currently being replayed.
/// @param _destination Chain ID of the destination chain.
/// @param _nonce Nonce of the message being relayed.
/// @param _sender Address of the user who sent the message.
/// @param _source Chain ID of the source chain.
/// @param _target Address that the message is targeted at.
/// @param _message Message to send to the target.
function relayMessage(
uint256 _destination,
uint256 _source,
uint256 _nonce,
address _sender,
address _target,
bytes calldata _message
)
external
payable;
/// @param _id Identifier of the SentMessage event to be relayed
/// @param _sentMessage Message payload of the `SentMessage` event
function relayMessage(ICrossL2Inbox.Identifier calldata _id, bytes calldata _sentMessage) external payable;
}
......@@ -122,8 +122,8 @@ library Hashing {
);
}
/// @notice Generates a unique hash for a message to be relayed across chains. This hash is
/// used to identify the message and ensure it is not relayed more than once.
/// @notice Generates a unique hash for cross l2 messages. This hash is used to identify
/// the message and ensure it is not relayed more than once.
/// @param _destination Chain ID of the destination chain.
/// @param _source Chain ID of the source chain.
/// @param _nonce Unique nonce associated with the message to prevent replay attacks.
......@@ -131,7 +131,7 @@ library Hashing {
/// @param _target Address of the contract or wallet that the message is targeting on the destination chain.
/// @param _message The message payload to be relayed to the target on the destination chain.
/// @return Hash of the encoded message parameters, used to uniquely identify the message.
function hashL2toL2CrossDomainMessengerRelayMessage(
function hashL2toL2CrossDomainMessage(
uint256 _destination,
uint256 _source,
uint256 _nonce,
......
......@@ -10,19 +10,20 @@ import { Predeploys } from "src/libraries/Predeploys.sol";
import { Hashing } from "src/libraries/Hashing.sol";
// Target contract
import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol";
import { ICrossL2Inbox } from "src/L2/interfaces/ICrossL2Inbox.sol";
import {
L2ToL2CrossDomainMessenger,
NotEntered,
MessageDestinationSameChain,
RelayMessageCallerNotCrossL2Inbox,
CrossL2InboxOriginNotL2ToL2CrossDomainMessenger,
IdOriginNotL2ToL2CrossDomainMessenger,
EventPayloadNotSentMessage,
MessageDestinationNotRelayChain,
MessageTargetCrossL2Inbox,
MessageTargetL2ToL2CrossDomainMessenger,
MessageAlreadyRelayed,
ReentrantCall
} from "src/L2/L2ToL2CrossDomainMessenger.sol";
import { CrossL2Inbox } from "src/L2/CrossL2Inbox.sol";
/// @title L2ToL2CrossDomainMessengerWithModifiableTransientStorage
/// @dev L2ToL2CrossDomainMessenger contract with methods to modify the transient storage.
......@@ -91,11 +92,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
vm.recordLogs();
// Call the sendMessage function
bytes32 msgHash =
l2ToL2CrossDomainMessenger.sendMessage({ _destination: _destination, _target: _target, _message: _message });
bytes32 msgHash = l2ToL2CrossDomainMessenger.sendMessage(_destination, _target, _message);
assertEq(
msgHash,
Hashing.hashL2toL2CrossDomainMessengerRelayMessage(
Hashing.hashL2toL2CrossDomainMessage(
_destination, block.chainid, messageNonce, address(this), _target, _message
)
);
......@@ -103,13 +103,15 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Check that the event was emitted with the correct parameters
Vm.Log[] memory logs = vm.getRecordedLogs();
assertEq(logs.length, 1);
assertEq(
logs[0].data,
abi.encodeCall(
L2ToL2CrossDomainMessenger.relayMessage,
(_destination, block.chainid, messageNonce, address(this), _target, _message)
)
);
// topics
assertEq(logs[0].topics[0], L2ToL2CrossDomainMessenger.SentMessage.selector);
assertEq(logs[0].topics[1], bytes32(_destination));
assertEq(logs[0].topics[2], bytes32(uint256(uint160(_target))));
assertEq(logs[0].topics[3], bytes32(messageNonce));
// data
assertEq(logs[0].data, abi.encode(address(this), _message));
// Check that the message nonce has been incremented
assertEq(l2ToL2CrossDomainMessenger.messageNonce(), messageNonce + 1);
......@@ -198,16 +200,15 @@ contract L2ToL2CrossDomainMessengerTest is Test {
address _sender,
address _target,
bytes calldata _message,
uint256 _value
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
// Ensure that the target contract is not a Forge contract.
assumeNotForgeAddress(_target);
// Ensure that the target contract is not CrossL2Inbox or L2ToL2CrossDomainMessenger
vm.assume(_target != Predeploys.CROSS_L2_INBOX);
vm.assume(_target != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
vm.assume(_target != Predeploys.CROSS_L2_INBOX && _target != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
// Ensure that the target call is payable if value is sent
if (_value > 0) assumePayable(_target);
......@@ -215,51 +216,68 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Ensure that the target contract does not revert
vm.mockCall({ callee: _target, msgValue: _value, data: _message, returnData: abi.encode(true) });
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
// Construct the SentMessage payload & identifier
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
// Look for correct emitted event
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.RelayedMessage(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
_source, _nonce, keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
);
// Ensure the target contract is called with the correct parameters
vm.expectCall({ callee: _target, msgValue: _value, data: _message });
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Call the relayMessage function
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid, // ensure the destination is the chain of L2ToL2CrossDomainMessenger
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
// Check that successfulMessages mapping updates the message hash correctly
// relay the message
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
assertEq(
l2ToL2CrossDomainMessenger.successfulMessages(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
),
true
);
}
// Check that entered slot is cleared after the function call
assertEq(l2ToL2CrossDomainMessenger.entered(), false);
function testFuzz_relayMessage_eventPayloadNotSentMessage_reverts(
uint256 _source,
uint256 _nonce,
bytes32 _msgHash,
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
// Expect a revert with the EventPayloadNotSentMessage selector
vm.expectRevert(EventPayloadNotSentMessage.selector);
// Check that metadata is cleared after the function call. We need to set the `entered` slot to non-zero value
// to prevent NotEntered revert when calling the crossDomainMessageSender and crossDomainMessageSource functions
l2ToL2CrossDomainMessenger.setEntered(1);
assertEq(l2ToL2CrossDomainMessenger.crossDomainMessageSource(), 0);
assertEq(l2ToL2CrossDomainMessenger.crossDomainMessageSender(), address(0));
// Point to a different remote log that the inbox validates
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage =
abi.encode(L2ToL2CrossDomainMessenger.RelayedMessage.selector, _source, _nonce, _msgHash);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
// Call
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
}
/// @dev Mock target function that checks the source and sender of the message in transient storage.
......@@ -281,7 +299,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
uint256 _source,
uint256 _nonce,
address _sender,
uint256 _value
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
......@@ -289,46 +310,39 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// contract has a non-zero balance. Thus, we set this contract's balance to zero and we hoax afterwards.
vm.deal(address(this), 0);
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Set the target and message for the reentrant call
address target = address(this);
bytes memory message = abi.encodeWithSelector(this.mockTarget.selector, _source, _sender);
bytes32 msgHash = keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message));
// Look for correct emitted event
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.RelayedMessage(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message))
);
emit L2ToL2CrossDomainMessenger.RelayedMessage(_source, _nonce, msgHash);
// Ensure the target contract is called with the correct parameters
vm.expectCall({ callee: target, msgValue: _value, data: message });
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Call the relayMessage function
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid, // ensure the destination is the chain of L2ToL2CrossDomainMessenger
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: target,
_message: message
// Construct and relay the message
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, target, _nonce), // topics
abi.encode(_sender, message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
// Check that successfulMessages mapping updates the message hash correctly
assertEq(
l2ToL2CrossDomainMessenger.successfulMessages(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, target, message))
),
true
);
assertEq(l2ToL2CrossDomainMessenger.successfulMessages(msgHash), true);
// Check that entered slot is cleared after the function call
assertEq(l2ToL2CrossDomainMessenger.entered(), false);
......@@ -353,14 +367,14 @@ contract L2ToL2CrossDomainMessengerTest is Test {
vm.expectRevert(ReentrantCall.selector);
l2ToL2CrossDomainMessenger.relayMessage({
_destination: block.chainid,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: address(0),
_message: ""
});
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, 1, 1, 1, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, address(0), _nonce), // topics
abi.encode(_sender, "") // data
);
l2ToL2CrossDomainMessenger.relayMessage(id, sentMessage);
// Ensure the function still reverts if `expectRevert` succeeds
revert();
......@@ -373,7 +387,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
uint256 _source2, // sender passed to `relayMessage` by the reentrant call.
address _sender2, // sender passed to `relayMessage` by the reentrant call.
uint256 _nonce,
uint256 _value
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
......@@ -381,13 +398,6 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// contract has a non-zero balance. Thus, we set this contract's balance to zero and we hoax afterwards.
vm.deal(address(this), 0);
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Set the target and message for the reentrant call
address target = address(this);
bytes memory message = abi.encodeWithSelector(this.mockTargetReentrant.selector, _source2, _nonce, _sender2);
......@@ -395,25 +405,30 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Look for correct emitted event
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.FailedRelayedMessage(
keccak256(abi.encode(block.chainid, _source1, _nonce, _sender1, target, message))
_source1, _nonce, keccak256(abi.encode(block.chainid, _source1, _nonce, _sender1, target, message))
);
// Ensure the target contract is called with the correct parameters
vm.expectCall({ callee: target, msgValue: _value, data: message });
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Call the relayMessage function
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid, // ensure the destination is the chain of L2ToL2CrossDomainMessenger
_source: _source1,
_nonce: _nonce,
_sender: _sender1,
_target: target,
_message: message
// Construct and relay the message
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source1);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, target, _nonce), // topics
abi.encode(_sender1, message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
// Check that entered slot is cleared after the function call
assertEq(l2ToL2CrossDomainMessenger.entered(), false);
......@@ -424,70 +439,36 @@ contract L2ToL2CrossDomainMessengerTest is Test {
assertEq(l2ToL2CrossDomainMessenger.crossDomainMessageSender(), address(0));
}
/// @dev Tests that the `relayMessage` function reverts when the caller is not the CrossL2Inbox contract.
function testFuzz_relayMessage_callerNotCrossL2Inbox_reverts(
uint256 _destination,
/// @dev Tests that the `relayMessage` function reverts when log identifier is not the cdm
function testFuzz_relayMessage_idOriginNotL2ToL2CrossDomainMessenger_reverts(
uint256 _source,
uint256 _nonce,
address _sender,
address _target,
bytes calldata _message,
uint256 _value
uint256 _value,
address _origin,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
// Add sufficient value to the contract to relay the message with
vm.deal(address(this), _value);
// Incorrect identifier origin
vm.assume(_origin != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
// Expect a revert with the RelayMessageCallerNotCrossL2Inbox selector
vm.expectRevert(RelayMessageCallerNotCrossL2Inbox.selector);
// Call `relayMessage` with the current contract as the caller to provoke revert
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: _destination,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
}
// Expect a revert with the IdOriginNotL2ToL2CrossDomainMessenger
vm.expectRevert(IdOriginNotL2ToL2CrossDomainMessenger.selector);
/// @dev Tests that the `relayMessage` function reverts when CrossL2Inbox's origin is not
/// L2ToL2CrossDomainMessenger.
function testFuzz_relayMessage_crossL2InboxOriginNotL2ToL2CrossDomainMessenger_reverts(
uint256 _destination,
uint256 _source,
uint256 _nonce,
address _sender,
address _target,
bytes calldata _message,
uint256 _value
)
external
{
// Set address(0) as the origin of the CrossL2Inbox contract, which is not the L2ToL2CrossDomainMessenger
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(address(0))
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Expect a revert with the CrossL2InboxOriginNotL2ToL2CrossDomainMessenger selector
vm.expectRevert(CrossL2InboxOriginNotL2ToL2CrossDomainMessenger.selector);
ICrossL2Inbox.Identifier memory id = ICrossL2Inbox.Identifier(_origin, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics
abi.encode(_sender, _message) // data
);
// Call `relayMessage` with invalid CrossL2Inbox origin to provoke revert
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: _destination,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
// Call
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
}
/// @dev Tests that the `relayMessage` function reverts when the destination is not the relay chain.
......@@ -498,35 +479,36 @@ contract L2ToL2CrossDomainMessengerTest is Test {
address _sender,
address _target,
bytes calldata _message,
uint256 _value
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
// Ensure the destination is not this chain
vm.assume(_destination != block.chainid);
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
// Expect a revert with the MessageDestinationNotRelayChain selector
vm.expectRevert(MessageDestinationNotRelayChain.selector);
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, _destination, _target, _nonce), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Expect a revert with the MessageDestinationNotRelayChain selector
vm.expectRevert(MessageDestinationNotRelayChain.selector);
// Call `relayMessage`
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: _destination,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
}
/// @dev Tests that the `relayMessage` function reverts when the message target is CrossL2Inbox.
......@@ -535,33 +517,37 @@ contract L2ToL2CrossDomainMessengerTest is Test {
uint256 _nonce,
address _sender,
bytes calldata _message,
uint256 _value
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Expect a revert with the MessageTargetCrossL2Inbox selector
vm.expectRevert(MessageTargetCrossL2Inbox.selector);
// Call `relayMessage` with CrossL2Inbox as the target to provoke revert. The current chain is the destination
// to prevent revert due to invalid destination
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: Predeploys.CROSS_L2_INBOX,
_message: _message
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(
L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, Predeploys.CROSS_L2_INBOX, _nonce
), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
// Call
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
}
/// @dev Tests that the `relayMessage` function reverts when the message target is L2ToL2CrossDomainMessenger.
......@@ -570,33 +556,39 @@ contract L2ToL2CrossDomainMessengerTest is Test {
uint256 _nonce,
address _sender,
bytes calldata _message,
uint256 _value
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Expect a revert with the MessageTargetL2ToL2CrossDomainMessenger selector
vm.expectRevert(MessageTargetL2ToL2CrossDomainMessenger.selector);
// Call `relayMessage` with L2ToL2CrossDomainMessenger as the target to provoke revert. The current chain is the
// destination to prevent revert due to invalid destination
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER,
_message: _message
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(
L2ToL2CrossDomainMessenger.SentMessage.selector,
block.chainid,
Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER,
_nonce
), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
}
/// @dev Tests that the `relayMessage` function reverts when the message has already been relayed.
......@@ -606,7 +598,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
address _sender,
address _target,
bytes calldata _message,
uint256 _value
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
......@@ -622,48 +617,37 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Ensure that the target contract does not revert
vm.mockCall({ callee: _target, msgValue: _value, data: _message, returnData: abi.encode(true) });
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Look for correct emitted event for first call.
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.RelayedMessage(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
_source, _nonce, keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
);
// First call to `relayMessage` should succeed. The current chain is the destination to prevent revert due to
// invalid destination
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// First call to `relayMessage` should succeed. The current chain is the destination to prevent revert due to
// invalid destination
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
// Second call should fail with MessageAlreadyRelayed selector
vm.expectRevert(MessageAlreadyRelayed.selector);
// Call `relayMessage` again. The current chain is the destination to prevent revert due to invalid destination
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
});
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
}
/// @dev Tests that the `relayMessage` function reverts when the target call fails.
......@@ -673,7 +657,10 @@ contract L2ToL2CrossDomainMessengerTest is Test {
address _sender,
address _target,
bytes calldata _message,
uint256 _value
uint256 _value,
uint256 _blockNum,
uint256 _logIndex,
uint256 _time
)
external
{
......@@ -686,30 +673,28 @@ contract L2ToL2CrossDomainMessengerTest is Test {
// Ensure that the target contract reverts
vm.mockCallRevert({ callee: _target, msgValue: _value, data: _message, revertData: abi.encode(false) });
// Mock the CrossL2Inbox origin to return the L2ToL2CrossDomainMessenger contract
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.origin.selector),
returnData: abi.encode(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER)
});
// Ensure caller is CrossL2Inbox to prevent a revert from the caller check and that it has sufficient value
hoax(Predeploys.CROSS_L2_INBOX, _value);
// Look for correct emitted event
vm.expectEmit(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER);
emit L2ToL2CrossDomainMessenger.FailedRelayedMessage(
keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
_source, _nonce, keccak256(abi.encode(block.chainid, _source, _nonce, _sender, _target, _message))
);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }({
_destination: block.chainid,
_source: _source,
_nonce: _nonce,
_sender: _sender,
_target: _target,
_message: _message
ICrossL2Inbox.Identifier memory id =
ICrossL2Inbox.Identifier(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _blockNum, _logIndex, _time, _source);
bytes memory sentMessage = abi.encodePacked(
abi.encode(L2ToL2CrossDomainMessenger.SentMessage.selector, block.chainid, _target, _nonce), // topics
abi.encode(_sender, _message) // data
);
// Ensure the CrossL2Inbox validates this message
vm.mockCall({
callee: Predeploys.CROSS_L2_INBOX,
data: abi.encodeWithSelector(CrossL2Inbox.validateMessage.selector, id, sentMessage),
returnData: ""
});
hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value);
l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage);
}
/// @dev Tests that the `crossDomainMessageSender` function returns the correct value.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment