Commit 826a7bdc authored by Diego's avatar Diego Committed by GitHub

feat/CrossL2Inbox: create (#10296)

* contracts-bedrock: create CrossL2Inbox

* contracts-bedrock: create ICrossL2Inbox

* contracts-bedrock: create tests for CrossL2Inbox

* contracts-bedrock: update CrossL2Inbox sol version to ^0.8.24

* contracts-bedrock: rename test to .t.sol ext

* contracts-bedrock: make snapshots

* contracts-bedrock: update semver-lock

* contracts-bedrock: drop snapshots for CrossL2InboxTest

* contracts-bedrock: update license for tests CrossL2Inbox

* contracts-bedrock: add CrossL2Inbox to predeploys

* contracts-bedrock: pin sol version of CrossL2Inbox to 0.8.25

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: add CROSS_L2_INBOX to predeploys

* contracts-bedrock: make slots internal in CrossL2Inbox

* contracts-bedrock: add custom errors to CrossL2Inbox

* contracts-bedrock: refactor tests for CrossL2Inbox

* contracts-bedrock: use TransientContext in CrossL2Inbox

* contracts-bedrock: fix L2Genesis test

* contracts-bedrock: minor tweaks to documentation in tests for CrossL2Inbox

* contracts-bedrock: relabel BLOCKNUMBER_SLOT to BLOCK_NUMBER_SLOT in CrossL2Inbox

* contracts-bedrock: update snapshots for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: improve documentation for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: fix tests for CrossL2Inbox

* contracts-bedrock: update modifier in CrossL2Inbox

* contracts-bedrock: drop arguments in custom errors for CrossL2Inbox

* contracts-bedrock: update snapshots for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: fix tests for CrossL2Inbox

* contracts-bedrock: remove redundant lines in CrossL2Inbox

* contracts-bedrock: add tests for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: minor improvements to tests for CrossL2Inbox

* contracts-bedrock: remove ENTERED_SLOT in CrossL2Inbox

* contracts-bedrock: remove CrossL2Inbox from isSupportedPredeploy

* contracts-bedrock: update semver-lock for CrossL2Inbox

* contracts-bedrock: update CrossL2Inbox with eip3074 specs

* contracts-bedrock: update tests for CrossL2Inbox with eip3074 specs

* contracts-bedrock: improve doc in tests for CrossL2Inbox

* contracts-bedrock: update snapshots for CrossL2Inbox

* contracts-bedrock: update semver-lock for CrossL2Inbox
parent 7e937296
......@@ -55,6 +55,10 @@
"initCodeHash": "0x2744d34573be83206d1b75d049d18a7bb37f9058e68c0803e5008c46b0dc2474",
"sourceCodeHash": "0xd787bd2a192ba5025fa0a8de2363af66a8de20de226e411bdb576adb64636cd0"
},
"src/L2/CrossL2Inbox.sol": {
"initCodeHash": "0x2455cab02c32f22521bfeb8a6e404c5ea2eca32e1bfdce8f34013e32d52ffc13",
"sourceCodeHash": "0x864d244dd0b01cb01e5f50425bedce46e72005bdb8bead198ac885481547f41d"
},
"src/L2/GasPriceOracle.sol": {
"initCodeHash": "0xfd456e91d8c9714590a4f0a2c1046ba70e102f1c629ead886c4eebc3f921c3c3",
"sourceCodeHash": "0xde06becce9514f46ba78b4cb0732c7a714d49ba8f131258d56a5f5b22b51be7e"
......
[
{
"inputs": [],
"name": "blockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "chainId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"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": "address",
"name": "_target",
"type": "address"
},
{
"internalType": "bytes",
"name": "_message",
"type": "bytes"
}
],
"name": "executeMessage",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "logIndex",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "origin",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "timestamp",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes",
"name": "encodedId",
"type": "bytes"
},
{
"indexed": false,
"internalType": "bytes",
"name": "message",
"type": "bytes"
}
],
"name": "ExecutingMessage",
"type": "event"
},
{
"inputs": [],
"name": "InvalidChainId",
"type": "error"
},
{
"inputs": [],
"name": "InvalidTimestamp",
"type": "error"
},
{
"inputs": [],
"name": "NotEntered",
"type": "error"
},
{
"inputs": [],
"name": "TargetCallFailed",
"type": "error"
}
]
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
import { Predeploys } from "src/libraries/Predeploys.sol";
import { TransientContext, TransientReentrancyAware } from "src/libraries/TransientContext.sol";
import { ISemver } from "src/universal/ISemver.sol";
import { ICrossL2Inbox } from "src/L2/ICrossL2Inbox.sol";
/// @title IDependencySet
/// @notice Interface for L1Block with only `isInDependencySet(uint256)` method.
interface IDependencySet {
/// @notice Returns true iff the chain associated with input chain ID is in the interop dependency set.
/// Every chain is in the interop dependency set of itself.
/// @param _chainId Input chain ID.
/// @return True if the input chain ID corresponds to a chain in the interop dependency set, and false otherwise.
function isInDependencySet(uint256 _chainId) external view returns (bool);
}
/// @notice Thrown when a non-written transient storage slot is attempted to be read from.
error NotEntered();
/// @notice Thrown when trying to execute a cross chain message with an invalid Identifier timestamp.
error InvalidTimestamp();
/// @notice Thrown when trying to execute a cross chain message with an invalid Identifier chain ID.
error InvalidChainId();
/// @notice Thrown when trying to execute a cross chain message and the target call fails.
error TargetCallFailed();
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000022
/// @title CrossL2Inbox
/// @notice The CrossL2Inbox is responsible for executing a cross chain message on the destination
/// chain. It is permissionless to execute a cross chain message on behalf of any user.
contract CrossL2Inbox is ICrossL2Inbox, ISemver, TransientReentrancyAware {
/// @notice Transient storage slot that the origin for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.origin")) - 1)
bytes32 internal constant ORIGIN_SLOT = 0xd2b7c5071ec59eb3ff0017d703a8ea513a7d0da4779b0dbefe845808c300c815;
/// @notice Transient storage slot that the blockNumber for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.blocknumber")) - 1)
bytes32 internal constant BLOCK_NUMBER_SLOT = 0x5a1da0738b7fdc60047c07bb519beb02aa32a8619de57e6258da1f1c2e020ccc;
/// @notice Transient storage slot that the logIndex for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.logindex")) - 1)
bytes32 internal constant LOG_INDEX_SLOT = 0xab8acc221aecea88a685fabca5b88bf3823b05f335b7b9f721ca7fe3ffb2c30d;
/// @notice Transient storage slot that the timestamp for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.timestamp")) - 1)
bytes32 internal constant TIMESTAMP_SLOT = 0x2e148a404a50bb94820b576997fd6450117132387be615e460fa8c5e11777e02;
/// @notice Transient storage slot that the chainId for an Identifier is stored at.
/// Equal to bytes32(uint256(keccak256("crossl2inbox.identifier.chainid")) - 1)
bytes32 internal constant CHAINID_SLOT = 0x6e0446e8b5098b8c8193f964f1b567ec3a2bdaeba33d36acb85c1f1d3f92d313;
/// @notice Semantic version.
/// @custom:semver 1.0.0
string public constant version = "1.0.0";
/// @notice Emitted when a cross chain message is being executed.
/// @param encodedId Encoded Identifier of the message.
/// @param message Message payload being executed.
event ExecutingMessage(bytes encodedId, bytes message);
/// @notice Enforces that cross domain message sender and source are set. Reverts if not.
/// Used to differentiate between 0 and nil in transient storage.
modifier notEntered() {
if (TransientContext.callDepth() == 0) revert NotEntered();
_;
}
/// @notice Returns the origin address of the Identifier. If not entered, reverts.
/// @return Origin address of the Identifier.
function origin() external view notEntered returns (address) {
return address(uint160(TransientContext.get(ORIGIN_SLOT)));
}
/// @notice Returns the block number of the Identifier. If not entered, reverts.
/// @return Block number of the Identifier.
function blockNumber() external view notEntered returns (uint256) {
return TransientContext.get(BLOCK_NUMBER_SLOT);
}
/// @notice Returns the log index of the Identifier. If not entered, reverts.
/// @return Log index of the Identifier.
function logIndex() external view notEntered returns (uint256) {
return TransientContext.get(LOG_INDEX_SLOT);
}
/// @notice Returns the timestamp of the Identifier. If not entered, reverts.
/// @return Timestamp of the Identifier.
function timestamp() external view notEntered returns (uint256) {
return TransientContext.get(TIMESTAMP_SLOT);
}
/// @notice Returns the chain ID of the Identifier. If not entered, reverts.
/// @return _chainId The chain ID of the Identifier.
function chainId() external view notEntered returns (uint256) {
return TransientContext.get(CHAINID_SLOT);
}
/// @notice Executes a cross chain message on the destination chain.
/// @param _id Identifier of the message.
/// @param _target Target address to call.
/// @param _message Message payload to call target with.
function executeMessage(
Identifier calldata _id,
address _target,
bytes memory _message
)
external
payable
reentrantAware
{
if (_id.timestamp > block.timestamp) revert InvalidTimestamp();
if (!IDependencySet(Predeploys.L1_BLOCK_ATTRIBUTES).isInDependencySet(_id.chainId)) {
revert InvalidChainId();
}
// Store the Identifier in transient storage.
_storeIdentifier(_id);
// Call the target account with the message payload.
bool success = _callWithAllGas(_target, _message);
// Revert if the target call failed.
if (!success) revert TargetCallFailed();
emit ExecutingMessage(abi.encode(_id), _message);
}
/// @notice Stores the Identifier in transient storage.
/// @param _id Identifier to store.
function _storeIdentifier(Identifier calldata _id) internal {
TransientContext.set(ORIGIN_SLOT, uint160(_id.origin));
TransientContext.set(BLOCK_NUMBER_SLOT, _id.blockNumber);
TransientContext.set(LOG_INDEX_SLOT, _id.logIndex);
TransientContext.set(TIMESTAMP_SLOT, _id.timestamp);
TransientContext.set(CHAINID_SLOT, _id.chainId);
}
/// @notice Calls the target address with the message payload and all available gas.
/// @param _target Target address to call.
/// @param _message Message payload to call target with.
/// @return _success True if the call was successful, and false otherwise.
function _callWithAllGas(address _target, bytes memory _message) internal returns (bool _success) {
assembly {
_success :=
call(
gas(), // gas
_target, // recipient
callvalue(), // ether value
add(_message, 32), // inloc
mload(_message), // inlen
0, // outloc
0 // outlen
)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @title ICrossL2Inbox
/// @notice Interface for the CrossL2Inbox contract.
interface ICrossL2Inbox {
/// @notice The struct for a pointer to a message payload in a remote (or local) chain.
struct Identifier {
address origin;
uint256 blockNumber;
uint256 logIndex;
uint256 timestamp;
uint256 chainId;
}
/// @notice Returns the origin address of the Identifier.
/// @return _origin The origin address of the Identifier.
function origin() external view returns (address _origin);
/// @notice Returns the block number of the Identifier.
/// @return _blockNumber The block number of the Identifier.
function blockNumber() external view returns (uint256 _blockNumber);
/// @notice Returns the log index of the Identifier.
/// @return _logIndex The log index of the Identifier.
function logIndex() external view returns (uint256 _logIndex);
/// @notice Returns the timestamp of the Identifier.
/// @return _timestamp The timestamp of the Identifier.
function timestamp() external view returns (uint256 _timestamp);
/// @notice Returns the chain ID of the Identifier.
/// @return _chainId The chain ID of the Identifier.
function chainId() external view returns (uint256 _chainId);
/// @notice Executes a cross chain message on the destination chain.
/// @param _id An Identifier pointing to the initiating message.
/// @param _target Account that is called with _msg.
/// @param _msg The message payload, matching the initiating message.
function executeMessage(
ICrossL2Inbox.Identifier calldata _id,
address _target,
bytes calldata _msg
)
external
payable;
}
......@@ -83,6 +83,9 @@ library Predeploys {
/// can no longer be accessed.
address internal constant LEGACY_ERC20_ETH = 0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000;
/// @notice Address of the CrossL2Inbox predeploy.
address internal constant CROSS_L2_INBOX = 0x4200000000000000000000000000000000000022;
/// @notice Returns the name of the predeploy at the given address.
function getName(address _addr) internal pure returns (string memory out_) {
require(isPredeployNamespace(_addr), "Predeploys: address must be a predeploy");
......@@ -107,6 +110,7 @@ library Predeploys {
if (_addr == EAS) return "EAS";
if (_addr == GOVERNANCE_TOKEN) return "GovernanceToken";
if (_addr == LEGACY_ERC20_ETH) return "LegacyERC20ETH";
if (_addr == CROSS_L2_INBOX) return "CrossL2Inbox";
revert("Predeploys: unnamed predeploy");
}
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.25;
// Testing utilities
import { Test } from "forge-std/Test.sol";
// Libraries
import { Predeploys } from "src/libraries/Predeploys.sol";
import { TransientContext } from "src/libraries/TransientContext.sol";
// Target contracts
import { CrossL2Inbox, NotEntered, InvalidTimestamp, InvalidChainId, TargetCallFailed } from "src/L2/CrossL2Inbox.sol";
import { ICrossL2Inbox } from "src/L2/ICrossL2Inbox.sol";
/// @title CrossL2InboxWithModifiableTransientStorage
/// @dev CrossL2Inbox contract with methods to modify the transient storage.
/// This is used to test the transient storage of CrossL2Inbox.
contract CrossL2InboxWithModifiableTransientStorage is CrossL2Inbox {
/// @dev Increments call depth in transient storage.
function increment() external {
TransientContext.increment();
}
/// @dev Sets origin in transient storage.
/// @param _origin Origin to set.
function setOrigin(address _origin) external {
TransientContext.set(ORIGIN_SLOT, uint160(_origin));
}
/// @dev Sets block number in transient storage.
/// @param _blockNumber Block number to set.
function setBlockNumber(uint256 _blockNumber) external {
TransientContext.set(BLOCK_NUMBER_SLOT, _blockNumber);
}
/// @dev Sets log index in transient storage.
/// @param _logIndex Log index to set.
function setLogIndex(uint256 _logIndex) external {
TransientContext.set(LOG_INDEX_SLOT, _logIndex);
}
/// @dev Sets timestamp in transient storage.
/// @param _timestamp Timestamp to set.
function setTimestamp(uint256 _timestamp) external {
TransientContext.set(TIMESTAMP_SLOT, _timestamp);
}
/// @dev Sets chain ID in transient storage.
/// @param _chainId Chain ID to set.
function setChainId(uint256 _chainId) external {
TransientContext.set(CHAINID_SLOT, _chainId);
}
}
/// @title CrossL2InboxTest
/// @dev Contract for testing the CrossL2Inbox contract.
contract CrossL2InboxTest is Test {
/// @dev Selector for the `isInDependencySet` method of the L1Block contract.
bytes4 constant L1BlockIsInDependencySetSelector = bytes4(keccak256("isInDependencySet(uint256)"));
/// @dev CrossL2Inbox contract instance.
CrossL2Inbox crossL2Inbox;
/// @dev Sets up the test suite.
function setUp() public {
// Deploy the L2ToL2CrossDomainMessenger contract
vm.etch(Predeploys.CROSS_L2_INBOX, address(new CrossL2InboxWithModifiableTransientStorage()).code);
crossL2Inbox = CrossL2Inbox(Predeploys.CROSS_L2_INBOX);
}
/// @dev Tests that the `executeMessage` function succeeds.
function testFuzz_executeMessage_succeeds(
ICrossL2Inbox.Identifier memory _id,
address _target,
bytes calldata _message,
uint256 _value
)
external
payable
{
// Ensure that the id's timestamp is valid (less than or equal to the current block timestamp)
_id.timestamp = bound(_id.timestamp, 0, block.timestamp);
// Ensure that the target call is payable if value is sent
if (_value > 0) assumePayable(_target);
// Ensure that the target call does not revert
vm.mockCall({ callee: _target, msgValue: _value, data: _message, returnData: abi.encode(true) });
// Ensure that the chain ID is in the dependency set
vm.mockCall({
callee: Predeploys.L1_BLOCK_ATTRIBUTES,
data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id.chainId),
returnData: abi.encode(true)
});
// Ensure that the contract has enough balance to send with value
vm.deal(address(this), _value);
// Look for the call to the target contract
vm.expectCall({ callee: _target, msgValue: _value, data: _message });
// Look for the emit ExecutingMessage event
vm.expectEmit(Predeploys.CROSS_L2_INBOX);
emit CrossL2Inbox.ExecutingMessage(abi.encode(_id), _message);
// Call the executeMessage function
crossL2Inbox.executeMessage{ value: _value }({ _id: _id, _target: _target, _message: _message });
// Check that the Identifier was stored correctly, but first we have to increment. This is because
// `executeMessage` increments + decrements the transient call depth, so we need to increment to have the
// getters use the right call depth.
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).increment();
assertEq(crossL2Inbox.origin(), _id.origin);
assertEq(crossL2Inbox.blockNumber(), _id.blockNumber);
assertEq(crossL2Inbox.logIndex(), _id.logIndex);
assertEq(crossL2Inbox.timestamp(), _id.timestamp);
assertEq(crossL2Inbox.chainId(), _id.chainId);
}
/// @dev Mock reentrant function that calls the `executeMessage` function.
/// @param _id Identifier to pass to the `executeMessage` function.
function mockReentrant(ICrossL2Inbox.Identifier calldata _id) external payable {
crossL2Inbox.executeMessage({ _id: _id, _target: address(0), _message: "" });
}
/// @dev Tests that the `executeMessage` function successfully handles reentrant calls.
function testFuzz_executeMessage_reentrant_succeeds(
ICrossL2Inbox.Identifier memory _id1, // identifier passed to `executeMessage` by the initial call.
ICrossL2Inbox.Identifier memory _id2, // identifier passed to `executeMessage` by the reentrant call.
uint256 _value
)
external
payable
{
// Ensure that the ids' timestamp are valid (less than or equal to the current block timestamp)
_id1.timestamp = bound(_id1.timestamp, 0, block.timestamp);
_id2.timestamp = bound(_id2.timestamp, 0, block.timestamp);
// Ensure that id1's chain ID is in the dependency set
vm.mockCall({
callee: Predeploys.L1_BLOCK_ATTRIBUTES,
data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id1.chainId),
returnData: abi.encode(true)
});
// Ensure that id2's chain ID is in the dependency set
vm.mockCall({
callee: Predeploys.L1_BLOCK_ATTRIBUTES,
data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id2.chainId),
returnData: abi.encode(true)
});
// Set the target and message for the reentrant call
address target = address(this);
bytes memory message = abi.encodeWithSelector(this.mockReentrant.selector, _id2);
// Ensure that the contract has enough balance to send with value
vm.deal(address(this), _value);
// Look for the call to the target contract
vm.expectCall({ callee: target, msgValue: _value, data: message });
// Call the executeMessage function
crossL2Inbox.executeMessage{ value: _value }({ _id: _id1, _target: target, _message: message });
// Check that the reentrant function didn't update Identifier in transient storage at first call's call depth
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).increment();
assertEq(crossL2Inbox.origin(), _id1.origin);
assertEq(crossL2Inbox.blockNumber(), _id1.blockNumber);
assertEq(crossL2Inbox.logIndex(), _id1.logIndex);
assertEq(crossL2Inbox.timestamp(), _id1.timestamp);
assertEq(crossL2Inbox.chainId(), _id1.chainId);
// Check that the reentrant function updated the Identifier at deeper call depth
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).increment();
assertEq(crossL2Inbox.origin(), _id2.origin);
assertEq(crossL2Inbox.blockNumber(), _id2.blockNumber);
assertEq(crossL2Inbox.logIndex(), _id2.logIndex);
assertEq(crossL2Inbox.timestamp(), _id2.timestamp);
assertEq(crossL2Inbox.chainId(), _id2.chainId);
}
/// @dev Tests that the `executeMessage` function reverts when called with an identifier with an invalid timestamp.
function testFuzz_executeMessage_invalidTimestamp_reverts(
ICrossL2Inbox.Identifier calldata _id,
address _target,
bytes calldata _message,
uint256 _value
)
external
{
// Ensure that the id's timestamp is invalid (greater than the current block timestamp)
vm.assume(_id.timestamp > block.timestamp);
// Ensure that the contract has enough balance to send with value
vm.deal(address(this), _value);
// Expect a revert with the InvalidTimestamp selector
vm.expectRevert(InvalidTimestamp.selector);
// Call the executeMessage function
crossL2Inbox.executeMessage{ value: _value }({ _id: _id, _target: _target, _message: _message });
}
/// @dev Tests that the `executeMessage` function reverts when called with an identifier with a chain ID not in
/// dependency set.
function testFuzz_executeMessage_invalidChainId_reverts(
ICrossL2Inbox.Identifier memory _id,
address _target,
bytes calldata _message,
uint256 _value
)
external
{
// Ensure that the id's timestamp is valid (less than or equal to the current block timestamp)
_id.timestamp = bound(_id.timestamp, 0, block.timestamp);
// Ensure that the chain ID is NOT in the dependency set
vm.mockCall({
callee: Predeploys.L1_BLOCK_ATTRIBUTES,
data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id.chainId),
returnData: abi.encode(false)
});
// Ensure that the contract has enough balance to send with value
vm.deal(address(this), _value);
// Expect a revert with the InvalidChainId selector
vm.expectRevert(InvalidChainId.selector);
// Call the executeMessage function
crossL2Inbox.executeMessage{ value: _value }({ _id: _id, _target: _target, _message: _message });
}
/// @dev Tests that the `executeMessage` function reverts when the target call fails.
function testFuzz_executeMessage_targetCallFailed_reverts(
ICrossL2Inbox.Identifier memory _id,
address _target,
bytes calldata _message,
uint256 _value
)
external
{
// Ensure that the id's timestamp is valid (less than or equal to the current block timestamp)
_id.timestamp = bound(_id.timestamp, 0, block.timestamp);
// Ensure that the target call is payable if value is sent
if (_value > 0) assumePayable(_target);
// Ensure that the target call reverts
vm.mockCallRevert({ callee: _target, msgValue: _value, data: _message, revertData: abi.encode(false) });
// Ensure that the chain ID is in the dependency set
vm.mockCall({
callee: Predeploys.L1_BLOCK_ATTRIBUTES,
data: abi.encodeWithSelector(L1BlockIsInDependencySetSelector, _id.chainId),
returnData: abi.encode(true)
});
// Ensure that the contract has enough balance to send with value
vm.deal(address(this), _value);
// Look for the call to the target contract
vm.expectCall({ callee: _target, msgValue: _value, data: _message });
// Expect a revert with the TargetCallFailed selector
vm.expectRevert(TargetCallFailed.selector);
// Call the executeMessage function
crossL2Inbox.executeMessage{ value: _value }({ _id: _id, _target: _target, _message: _message });
}
/// @dev Tests that the `origin` function returns the correct value.
function testFuzz_origin_succeeds(address _origin) external {
// Increment the call depth to prevent NotEntered revert
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).increment();
// Set origin in the transient storage
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).setOrigin(_origin);
// Check that the `origin` function returns the correct value
assertEq(crossL2Inbox.origin(), _origin);
}
/// @dev Tests that the `origin` function reverts when not entered.
function test_origin_notEntered_reverts() external {
// Expect a revert with the NotEntered selector
vm.expectRevert(NotEntered.selector);
// Call the `origin` function
crossL2Inbox.origin();
}
/// @dev Tests that the `blockNumber` function returns the correct value.
function testFuzz_blockNumber_succeeds(uint256 _blockNumber) external {
// Increment the call depth to prevent NotEntered revert
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).increment();
// Set blockNumber in the transient storage
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).setBlockNumber(_blockNumber);
// Check that the `blockNumber` function returns the correct value
assertEq(crossL2Inbox.blockNumber(), _blockNumber);
}
/// @dev Tests that the `blockNumber` function reverts when not entered.
function test_blockNumber_notEntered_reverts() external {
// Expect a revert with the NotEntered selector
vm.expectRevert(NotEntered.selector);
// Call the `blockNumber` function
crossL2Inbox.blockNumber();
}
/// @dev Tests that the `logIndex` function returns the correct value.
function testFuzz_logIndex_succeeds(uint256 _logIndex) external {
// Increment the call depth to prevent NotEntered revert
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).increment();
// Set logIndex in the transient storage
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).setLogIndex(_logIndex);
// Check that the `logIndex` function returns the correct value
assertEq(crossL2Inbox.logIndex(), _logIndex);
}
/// @dev Tests that the `logIndex` function reverts when not entered.
function test_logIndex_notEntered_reverts() external {
// Expect a revert with the NotEntered selector
vm.expectRevert(NotEntered.selector);
// Call the `logIndex` function
crossL2Inbox.logIndex();
}
/// @dev Tests that the `timestamp` function returns the correct value.
function testFuzz_timestamp_succeeds(uint256 _timestamp) external {
// Increment the call depth to prevent NotEntered revert
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).increment();
// Set timestamp in the transient storage
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).setTimestamp(_timestamp);
// Check that the `timestamp` function returns the correct value
assertEq(crossL2Inbox.timestamp(), _timestamp);
}
/// @dev Tests that the `timestamp` function reverts when not entered.
function test_timestamp_notEntered_reverts() external {
// Expect a revert with the NotEntered selector
vm.expectRevert(NotEntered.selector);
// Call the `timestamp` function
crossL2Inbox.timestamp();
}
/// @dev Tests that the `chainId` function returns the correct value.
function testFuzz_chainId_succeeds(uint256 _chainId) external {
// Increment the call depth to prevent NotEntered revert
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).increment();
// Set chainId in the transient storage
CrossL2InboxWithModifiableTransientStorage(Predeploys.CROSS_L2_INBOX).setChainId(_chainId);
// Check that the `chainId` function returns the correct value
assertEq(crossL2Inbox.chainId(), _chainId);
}
/// @dev Tests that the `chainId` function reverts when not entered.
function test_chainId_notEntered_reverts() external {
// Expect a revert with the NotEntered selector
vm.expectRevert(NotEntered.selector);
// Call the `chainId` function
crossL2Inbox.chainId();
}
}
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