diff --git a/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol index 89311cf18f01..58f07a9c92d9 100644 --- a/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/interfaces/L2/IL2ToL2CrossDomainMessenger.sol @@ -104,7 +104,14 @@ interface IL2ToL2CrossDomainMessenger { /// 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(Identifier calldata _id, bytes calldata _sentMessage) external payable; + /// @return returnData_ Return data from the target contract call. + function relayMessage( + Identifier calldata _id, + bytes calldata _sentMessage + ) + external + payable + returns (bytes memory returnData_); function messageVersion() external view returns (uint16); diff --git a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json index 9f51cc1c3a27..3d381ec9ae51 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json @@ -110,7 +110,13 @@ } ], "name": "relayMessage", - "outputs": [], + "outputs": [ + { + "internalType": "bytes", + "name": "returnData_", + "type": "bytes" + } + ], "stateMutability": "payable", "type": "function" }, diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index 2878c4117c40..609f656d4802 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -100,8 +100,8 @@ "sourceCodeHash": "0xaef8ea36c5b78cd12e0e62811d51db627ccf0dfd2cc5479fb707a10ef0d42048" }, "src/L2/L2ToL2CrossDomainMessenger.sol": { - "initCodeHash": "0x45564b97c63419cc12eadc60425c6d001857a3eea688ecaf1439ae7ede6aa9aa", - "sourceCodeHash": "0xed64736338b43a42f6bc6a88cca734403e1bb9ceafa55e4738605dfdedd1a99f" + "initCodeHash": "0xc56db8cb569efa0467fd53ab3fa218af3051e54f5517d7fafb7b5831b4350618", + "sourceCodeHash": "0x72062343a044e9c56f4143dcfc71706286eb205902006c2afcf6a4cd90c3e9f8" }, "src/L2/OptimismSuperchainERC20.sol": { "initCodeHash": "0xdac32a1057a6bc8a8d2ffdce1db8f34950cd0ffd1454d2133865736d21869192", diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index c7ca4e1eba49..fc1f74e99c53 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -5,7 +5,6 @@ pragma solidity 0.8.25; import { Encoding } from "src/libraries/Encoding.sol"; import { Hashing } from "src/libraries/Hashing.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { SafeCall } from "src/libraries/SafeCall.sol"; import { TransientReentrancyAware } from "src/libraries/TransientContext.sol"; // Interfaces @@ -72,8 +71,8 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { uint16 public constant messageVersion = uint16(0); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.13 - string public constant version = "1.0.0-beta.13"; + /// @custom:semver 1.0.0-beta.14 + string public constant version = "1.0.0-beta.14"; /// @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. @@ -160,7 +159,15 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { /// @param _id Identifier of the SentMessage event to be relayed /// @param _sentMessage Message payload of the `SentMessage` event /// @return returnData_ Return data from the target contract call. - function relayMessage(Identifier calldata _id, bytes calldata _sentMessage) external payable nonReentrant returns (bytes memory returnData_) { + function relayMessage( + Identifier calldata _id, + bytes calldata _sentMessage + ) + external + payable + nonReentrant + returns (bytes memory returnData_) + { // 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) { @@ -196,7 +203,7 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { _storeMessageMetadata(source, sender); bool success; - (success, returnData_) = target.call{value: msg.value}(message); + (success, returnData_) = target.call{ value: msg.value }(message); if (!success) { revert TargetCallFailed(); diff --git a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol index 0c064f686055..7c6a57ea586b 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol @@ -393,6 +393,48 @@ contract L2ToL2CrossDomainMessengerTest is Test { assertEq(l2ToL2CrossDomainMessenger.crossDomainMessageSender(), address(0)); } + /// @dev Tests the `relayMessage` function returns the expected return data of the call to the target contract. + function testFuzz_relayMessage_returnData_succeeds( + uint256 _source, + uint256 _nonce, + address _sender, + uint256 _value, + uint256 _blockNum, + uint256 _logIndex, + uint256 _time, + address _target, + bytes memory _mockedReturnData + ) + public + { + // Declare a random call to be made over the target + bytes memory message = abi.encodeWithSignature("randomCall()"); + + // Construct the message + Identifier memory id = + 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.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(message))), + returnData: "" + }); + + // Mock the random call over the target with the expected return data + vm.mockCall({ callee: _target, data: message, returnData: _mockedReturnData }); + + hoax(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, _value); + bytes memory returnData = l2ToL2CrossDomainMessenger.relayMessage{ value: _value }(id, sentMessage); + + // Check that the return data is the mocked one + assertEq(returnData, _mockedReturnData); + } + /// @dev Mock reentrant function that calls the `relayMessage` function. /// @param _source Source chain ID of the message. /// @param _nonce Nonce of the message.