diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 61a067d80987..476c37fc2cf6 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -104,8 +104,8 @@ "sourceCodeHash": "0xd08a2e6514dbd44e16aa312a1b27b2841a9eab5622cbd05a39c30f543fad673c" }, "src/L2/L2ToL2CrossDomainMessenger.sol": { - "initCodeHash": "0x6f19eb8ff0950156b65cd92872240c0153ac5f3b6f0861d57bf561fdbcacbeac", - "sourceCodeHash": "0xfea53344596d735eff3be945ed1300dc75a6f8b7b2c02c0043af5b0036f5f239" + "initCodeHash": "0xf760d814018281b36d9a6a0ab16a23348effb33cf0ab299e3022b59283e46160", + "sourceCodeHash": "0xe8d99e4702d90814089c4a80e259c891a95f6d4750c7220fc6b2672c26ef2700" }, "src/L2/OptimismSuperchainERC20.sol": { "initCodeHash": "0xd5c84e45746fd741d541a917ddc1cc0c7043c6b21d5c18040d4bc999d6a7b2db", @@ -128,8 +128,8 @@ "sourceCodeHash": "0x75d061633a141af11a19b86e599a1725dfae8d245dcddfb6bb244a50d5e53f96" }, "src/L2/SuperchainTokenBridge.sol": { - "initCodeHash": "0x07fc1d495928d9c13bd945a049d17e1d105d01c2082a7719e5d18cbc0e1c7d9e", - "sourceCodeHash": "0xaf2458e48dcadcafa8940cde7368549eae2280eef91247600d864ddac20f5d82" + "initCodeHash": "0xef7590c30630a75f105384e339e52758569c25a5aa0a5934c521e004b8f86220", + "sourceCodeHash": "0x4f539e9d9096d31e861982b8f751fa2d7de0849590523375cf92e175294d1036" }, "src/L2/SuperchainWETH.sol": { "initCodeHash": "0x50f6ea9bfe650fcf792e98e44b1bf66c036fd0e6d4b753da680253d7d8609816", diff --git a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json index 2676f90b0491..f6d2692bae53 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json @@ -1,4 +1,22 @@ [ + { + "inputs": [], + "name": "crossDomainMessageContext", + "outputs": [ + { + "internalType": "address", + "name": "sender_", + "type": "address" + }, + { + "internalType": "uint256", + "name": "source_", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "crossDomainMessageSender", diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index 4c1ffc38760f..0d7b46080fc9 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -64,8 +64,8 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra uint16 public constant messageVersion = uint16(0); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.7 - string public constant version = "1.0.0-beta.7"; + /// @custom:semver 1.0.0-beta.8 + string public constant version = "1.0.0-beta.8"; /// @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. @@ -114,6 +114,16 @@ contract L2ToL2CrossDomainMessenger is IL2ToL2CrossDomainMessenger, ISemver, Tra } } + /// @notice Retrieves the context of the current cross domain message. If not entered, reverts. + /// @return sender_ Address of the sender of the current cross domain message. + /// @return source_ Chain ID of the source of the current cross domain message. + function crossDomainMessageContext() external view onlyEntered returns (address sender_, uint256 source_) { + assembly { + sender_ := tload(CROSS_DOMAIN_MESSAGE_SENDER_SLOT) + source_ := tload(CROSS_DOMAIN_MESSAGE_SOURCE_SLOT) + } + } + /// @notice Sends a message to some target address on a destination chain. Note that if the call always reverts, /// then the message will be unrelayable and any ETH sent will be permanently locked. The same will occur /// if the target on the other chain is considered unsafe (see the _isUnsafeTarget() function). diff --git a/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol b/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol index 1dcc233638f5..284beb79ec0e 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainTokenBridge.sol @@ -42,8 +42,8 @@ contract SuperchainTokenBridge { address internal constant MESSENGER = Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER; /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.1 - string public constant version = "1.0.0-beta.1"; + /// @custom:semver 1.0.0-beta.2 + string public constant version = "1.0.0-beta.2"; /// @notice Sends tokens to a target address on another chain. /// @dev Tokens are burned on the source chain. @@ -80,11 +80,9 @@ contract SuperchainTokenBridge { function relayERC20(address _token, address _from, address _to, uint256 _amount) external { if (msg.sender != MESSENGER) revert Unauthorized(); - if (IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageSender() != address(this)) { - revert InvalidCrossDomainSender(); - } - - uint256 source = IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageSource(); + (address crossDomainMessageSender, uint256 source) = + IL2ToL2CrossDomainMessenger(MESSENGER).crossDomainMessageContext(); + if (crossDomainMessageSender != address(this)) revert InvalidCrossDomainSender(); ISuperchainERC20(_token).crosschainMint(_to, _amount); diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol index 2b5b945dec73..f8a803d78be8 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol @@ -27,6 +27,11 @@ interface IL2ToL2CrossDomainMessenger { /// @return source_ Chain ID of the source of the current cross domain message. function crossDomainMessageSource() external view returns (uint256 source_); + /// @notice Retrieves the context of the current cross domain message. If not entered, reverts. + /// @return sender_ Address of the sender of the current cross domain message. + /// @return source_ Chain ID of the source of the current cross domain message. + function crossDomainMessageContext() external view returns (address sender_, uint256 source_); + /// @notice Sends a message to some target address on a destination chain. Note that if the call /// always reverts, then the message will be unrelayable, and any ETH sent will be /// permanently locked. The same will occur if the target on the other chain is diff --git a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol index f5ff43c832ca..e0149e8aab78 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol @@ -744,4 +744,34 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Call `crossDomainMessageSource` to provoke revert l2ToL2CrossDomainMessenger.crossDomainMessageSource(); } + + /// @dev Tests that the `crossDomainMessageContext` function returns the correct value. + function testFuzz_crossDomainMessageContext_succeeds(address _sender, uint256 _source) external { + // Set `entered` to non-zero value to prevent NotEntered revert + l2ToL2CrossDomainMessenger.setEntered(1); + // Ensure that the contract is now entered + assertEq(l2ToL2CrossDomainMessenger.entered(), true); + + // Set cross domain message source in the transient storage + l2ToL2CrossDomainMessenger.setCrossDomainMessageSender(_sender); + l2ToL2CrossDomainMessenger.setCrossDomainMessageSource(_source); + + // Check that the `crossDomainMessageContext` function returns the correct value + (address crossDomainContextSender, uint256 crossDomainContextSource) = + l2ToL2CrossDomainMessenger.crossDomainMessageContext(); + assertEq(crossDomainContextSender, _sender); + assertEq(crossDomainContextSource, _source); + } + + /// @dev Tests that the `crossDomainMessageContext` function reverts when not entered. + function test_crossDomainMessageContext_notEntered_reverts() external { + // Ensure that the contract is not entered + assertEq(l2ToL2CrossDomainMessenger.entered(), false); + + // Expect a revert with the NotEntered selector + vm.expectRevert(NotEntered.selector); + + // Call `crossDomainMessageContext` to provoke revert + l2ToL2CrossDomainMessenger.crossDomainMessageContext(); + } } diff --git a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol index 8367112a5942..e40904324d0c 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainTokenBridge.t.sol @@ -139,6 +139,7 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { function testFuzz_relayERC20_notCrossDomainSender_reverts( address _token, address _crossDomainMessageSender, + uint256 _source, address _to, uint256 _amount ) @@ -146,11 +147,11 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { { vm.assume(_crossDomainMessageSender != address(superchainTokenBridge)); - // Mock the call over the `crossDomainMessageSender` function setting a wrong sender + // Mock the call over the `crossDomainMessageContext` function setting a wrong sender vm.mockCall( Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, - abi.encodeWithSelector(IL2ToL2CrossDomainMessenger.crossDomainMessageSender.selector), - abi.encode(_crossDomainMessageSender) + abi.encodeWithSelector(IL2ToL2CrossDomainMessenger.crossDomainMessageContext.selector), + abi.encode(_crossDomainMessageSender, _source) ); // Expect the revert with `InvalidCrossDomainSender` selector @@ -165,18 +166,11 @@ contract SuperchainTokenBridgeTest is Bridge_Initializer { function testFuzz_relayERC20_succeeds(address _from, address _to, uint256 _amount, uint256 _source) public { vm.assume(_to != ZERO_ADDRESS); - // Mock the call over the `crossDomainMessageSender` function setting the same address as value + // Mock the call over the `crossDomainMessageContext` function setting the same address as value _mockAndExpect( Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, - abi.encodeWithSelector(IL2ToL2CrossDomainMessenger.crossDomainMessageSender.selector), - abi.encode(address(superchainTokenBridge)) - ); - - // Mock the call over the `crossDomainMessageSource` function setting the source chain ID as value - _mockAndExpect( - Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, - abi.encodeWithSelector(IL2ToL2CrossDomainMessenger.crossDomainMessageSource.selector), - abi.encode(_source) + abi.encodeWithSelector(IL2ToL2CrossDomainMessenger.crossDomainMessageContext.selector), + abi.encode(address(superchainTokenBridge), _source) ); // Get the total supply and balance of `_to` before the relay to compare later on the assertions