Commit d37d753d authored by Andreas Bigger's avatar Andreas Bigger

Port all L2 contracts and tests to use the triple slash natspec styling.

parent e1571d74
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -4,23 +4,19 @@ pragma solidity 0.8.15; ...@@ -4,23 +4,19 @@ pragma solidity 0.8.15;
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
import { FeeVault } from "../universal/FeeVault.sol"; import { FeeVault } from "../universal/FeeVault.sol";
/** /// @custom:proxied
* @custom:proxied /// @custom:predeploy 0x4200000000000000000000000000000000000019
* @custom:predeploy 0x4200000000000000000000000000000000000019 /// @title BaseFeeVault
* @title BaseFeeVault /// @notice The BaseFeeVault accumulates the base fee that is paid by transactions.
* @notice The BaseFeeVault accumulates the base fee that is paid by transactions.
*/
contract BaseFeeVault is FeeVault, Semver { contract BaseFeeVault is FeeVault, Semver {
/** /// @custom:semver 1.2.1
* @custom:semver 1.2.0 /// @notice Constructs the BaseFeeVault contract.
* /// @param _recipient Wallet that will receive the fees.
* @param _recipient Wallet that will receive the fees. /// @param _minWithdrawalAmount Minimum balance for withdrawals.
* @param _minWithdrawalAmount Minimum balance for withdrawals. /// @param _withdrawalNetwork Network which the recipient will receive fees on.
* @param _withdrawalNetwork Network which the recipient will receive fees on.
*/
constructor( constructor(
address _recipient, address _recipient,
uint256 _minWithdrawalAmount, uint256 _minWithdrawalAmount,
WithdrawalNetwork _withdrawalNetwork WithdrawalNetwork _withdrawalNetwork
) FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) Semver(1, 2, 0) {} ) FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) Semver(1, 2, 1) {}
} }
...@@ -4,18 +4,14 @@ pragma solidity ^0.8.0; ...@@ -4,18 +4,14 @@ pragma solidity ^0.8.0;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
/** /// @title CrossDomainOwnable
* @title CrossDomainOwnable /// @notice This contract extends the OpenZeppelin `Ownable` contract for L2 contracts to be owned
* @notice This contract extends the OpenZeppelin `Ownable` contract for L2 contracts to be owned /// by contracts on L1. Note that this contract is only safe to be used if the
* by contracts on L1. Note that this contract is only safe to be used if the /// CrossDomainMessenger system is bypassed and the caller on L1 is calling the
* CrossDomainMessenger system is bypassed and the caller on L1 is calling the /// OptimismPortal directly.
* OptimismPortal directly.
*/
abstract contract CrossDomainOwnable is Ownable { abstract contract CrossDomainOwnable is Ownable {
/** /// @notice Overrides the implementation of the `onlyOwner` modifier to check that the unaliased
* @notice Overrides the implementation of the `onlyOwner` modifier to check that the unaliased /// `msg.sender` is the owner of the contract.
* `msg.sender` is the owner of the contract.
*/
function _checkOwner() internal view override { function _checkOwner() internal view override {
require( require(
owner() == AddressAliasHelper.undoL1ToL2Alias(msg.sender), owner() == AddressAliasHelper.undoL1ToL2Alias(msg.sender),
......
...@@ -5,19 +5,15 @@ import { Predeploys } from "../libraries/Predeploys.sol"; ...@@ -5,19 +5,15 @@ import { Predeploys } from "../libraries/Predeploys.sol";
import { L2CrossDomainMessenger } from "./L2CrossDomainMessenger.sol"; import { L2CrossDomainMessenger } from "./L2CrossDomainMessenger.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/** /// @title CrossDomainOwnable2
* @title CrossDomainOwnable2 /// @notice This contract extends the OpenZeppelin `Ownable` contract for L2 contracts to be owned
* @notice This contract extends the OpenZeppelin `Ownable` contract for L2 contracts to be owned /// by contracts on L1. Note that this contract is meant to be used with systems that use
* by contracts on L1. Note that this contract is meant to be used with systems that use /// the CrossDomainMessenger system. It will not work if the OptimismPortal is used
* the CrossDomainMessenger system. It will not work if the OptimismPortal is used /// directly.
* directly.
*/
abstract contract CrossDomainOwnable2 is Ownable { abstract contract CrossDomainOwnable2 is Ownable {
/** /// @notice Overrides the implementation of the `onlyOwner` modifier to check that the unaliased
* @notice Overrides the implementation of the `onlyOwner` modifier to check that the unaliased /// `xDomainMessageSender` is the owner of the contract. This value is set to the caller
* `xDomainMessageSender` is the owner of the contract. This value is set to the caller /// of the L1CrossDomainMessenger.
* of the L1CrossDomainMessenger.
*/
function _checkOwner() internal view override { function _checkOwner() internal view override {
L2CrossDomainMessenger messenger = L2CrossDomainMessenger( L2CrossDomainMessenger messenger = L2CrossDomainMessenger(
Predeploys.L2_CROSS_DOMAIN_MESSENGER Predeploys.L2_CROSS_DOMAIN_MESSENGER
......
...@@ -5,35 +5,30 @@ import { Predeploys } from "../libraries/Predeploys.sol"; ...@@ -5,35 +5,30 @@ import { Predeploys } from "../libraries/Predeploys.sol";
import { L2CrossDomainMessenger } from "./L2CrossDomainMessenger.sol"; import { L2CrossDomainMessenger } from "./L2CrossDomainMessenger.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/** /// @title CrossDomainOwnable3
* @title CrossDomainOwnable3 /// @notice This contract extends the OpenZeppelin `Ownable` contract for L2 contracts to be owned
* @notice This contract extends the OpenZeppelin `Ownable` contract for L2 contracts to be owned /// by contracts on either L1 or L2. Note that this contract is meant to be used with
* by contracts on either L1 or L2. Note that this contract is meant to be used with systems /// systems that use the CrossDomainMessenger system. It will not work if the
* that use the CrossDomainMessenger system. It will not work if the OptimismPortal is /// OptimismPortal is used directly.
* used directly.
*/
abstract contract CrossDomainOwnable3 is Ownable { abstract contract CrossDomainOwnable3 is Ownable {
/** /// @notice If true, the contract uses the cross domain _checkOwner function override.
* @notice If true, the contract uses the cross domain _checkOwner function override. If false /// If false it uses the standard Ownable _checkOwner function.
* it uses the standard Ownable _checkOwner function.
*/
bool public isLocal = true; bool public isLocal = true;
/** /// @notice Emits when ownership of the contract is transferred. Includes the
* @notice Emits when ownership of the contract is transferred. Includes the /// isLocal field in addition to the standard `Ownable` OwnershipTransferred event.
* isLocal field in addition to the standard `Ownable` OwnershipTransferred event. /// @param previousOwner The previous owner of the contract.
*/ /// @param newOwner The new owner of the contract.
/// @param isLocal Configures the `isLocal` contract variable.
event OwnershipTransferred( event OwnershipTransferred(
address indexed previousOwner, address indexed previousOwner,
address indexed newOwner, address indexed newOwner,
bool isLocal bool isLocal
); );
/** /// @notice Allows for ownership to be transferred with specifying the locality.
* @notice Allows for ownership to be transferred with specifying the locality. /// @param _owner The new owner of the contract.
* @param _owner The new owner of the contract. /// @param _isLocal Configures the locality of the ownership.
* @param _isLocal Configures the locality of the ownership.
*/
function transferOwnership(address _owner, bool _isLocal) external onlyOwner { function transferOwnership(address _owner, bool _isLocal) external onlyOwner {
require(_owner != address(0), "CrossDomainOwnable3: new owner is the zero address"); require(_owner != address(0), "CrossDomainOwnable3: new owner is the zero address");
...@@ -44,11 +39,9 @@ abstract contract CrossDomainOwnable3 is Ownable { ...@@ -44,11 +39,9 @@ abstract contract CrossDomainOwnable3 is Ownable {
emit OwnershipTransferred(oldOwner, _owner, _isLocal); emit OwnershipTransferred(oldOwner, _owner, _isLocal);
} }
/** /// @notice Overrides the implementation of the `onlyOwner` modifier to check that the unaliased
* @notice Overrides the implementation of the `onlyOwner` modifier to check that the unaliased /// `xDomainMessageSender` is the owner of the contract. This value is set to the caller
* `xDomainMessageSender` is the owner of the contract. This value is set to the caller /// of the L1CrossDomainMessenger.
* of the L1CrossDomainMessenger.
*/
function _checkOwner() internal view override { function _checkOwner() internal view override {
if (isLocal) { if (isLocal) {
require(owner() == msg.sender, "CrossDomainOwnable3: caller is not the owner"); require(owner() == msg.sender, "CrossDomainOwnable3: caller is not the owner");
......
...@@ -5,41 +5,32 @@ import { Semver } from "../universal/Semver.sol"; ...@@ -5,41 +5,32 @@ import { Semver } from "../universal/Semver.sol";
import { Predeploys } from "../libraries/Predeploys.sol"; import { Predeploys } from "../libraries/Predeploys.sol";
import { L1Block } from "../L2/L1Block.sol"; import { L1Block } from "../L2/L1Block.sol";
/** /// @custom:proxied
* @custom:proxied /// @custom:predeploy 0x420000000000000000000000000000000000000F
* @custom:predeploy 0x420000000000000000000000000000000000000F /// @title GasPriceOracle
* @title GasPriceOracle /// @notice This contract maintains the variables responsible for computing the L1 portion of the
* @notice This contract maintains the variables responsible for computing the L1 portion of the /// total fee charged on L2. Before Bedrock, this contract held variables in state that were
* total fee charged on L2. Before Bedrock, this contract held variables in state that were /// read during the state transition function to compute the L1 portion of the transaction
* read during the state transition function to compute the L1 portion of the transaction /// fee. After Bedrock, this contract now simply proxies the L1Block contract, which has
* fee. After Bedrock, this contract now simply proxies the L1Block contract, which has /// the values used to compute the L1 portion of the fee in its state.
* the values used to compute the L1 portion of the fee in its state. ///
* /// The contract exposes an API that is useful for knowing how large the L1 portion of the
* The contract exposes an API that is useful for knowing how large the L1 portion of the /// transaction fee will be. The following events were deprecated with Bedrock:
* transaction fee will be. The following events were deprecated with Bedrock: /// - event OverheadUpdated(uint256 overhead);
* - event OverheadUpdated(uint256 overhead); /// - event ScalarUpdated(uint256 scalar);
* - event ScalarUpdated(uint256 scalar); /// - event DecimalsUpdated(uint256 decimals);
* - event DecimalsUpdated(uint256 decimals);
*/
contract GasPriceOracle is Semver { contract GasPriceOracle is Semver {
/** /// @notice Number of decimals used in the scalar.
* @notice Number of decimals used in the scalar.
*/
uint256 public constant DECIMALS = 6; uint256 public constant DECIMALS = 6;
/** /// @custom:semver 1.0.1
* @custom:semver 1.0.0 /// @notice Constructs the GasPriceOracle contract.
*/ constructor() Semver(1, 0, 1) {}
constructor() Semver(1, 0, 0) {}
/** /// @notice Computes the L1 portion of the fee based on the size of the rlp encoded input
* @notice Computes the L1 portion of the fee based on the size of the rlp encoded input /// transaction, the current L1 base fee, and the various dynamic parameters.
* transaction, the current L1 base fee, and the various dynamic parameters. /// @param _data Unsigned fully RLP-encoded transaction to get the L1 fee for.
* /// @return L1 fee that should be paid for the tx
* @param _data Unsigned fully RLP-encoded transaction to get the L1 fee for.
*
* @return L1 fee that should be paid for the tx
*/
function getL1Fee(bytes memory _data) external view returns (uint256) { function getL1Fee(bytes memory _data) external view returns (uint256) {
uint256 l1GasUsed = getL1GasUsed(_data); uint256 l1GasUsed = getL1GasUsed(_data);
uint256 l1Fee = l1GasUsed * l1BaseFee(); uint256 l1Fee = l1GasUsed * l1BaseFee();
...@@ -49,71 +40,49 @@ contract GasPriceOracle is Semver { ...@@ -49,71 +40,49 @@ contract GasPriceOracle is Semver {
return scaled; return scaled;
} }
/** /// @notice Retrieves the current gas price (base fee).
* @notice Retrieves the current gas price (base fee). /// @return Current L2 gas price (base fee).
*
* @return Current L2 gas price (base fee).
*/
function gasPrice() public view returns (uint256) { function gasPrice() public view returns (uint256) {
return block.basefee; return block.basefee;
} }
/** /// @notice Retrieves the current base fee.
* @notice Retrieves the current base fee. /// @return Current L2 base fee.
*
* @return Current L2 base fee.
*/
function baseFee() public view returns (uint256) { function baseFee() public view returns (uint256) {
return block.basefee; return block.basefee;
} }
/** /// @notice Retrieves the current fee overhead.
* @notice Retrieves the current fee overhead. /// @return Current fee overhead.
*
* @return Current fee overhead.
*/
function overhead() public view returns (uint256) { function overhead() public view returns (uint256) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeOverhead(); return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeOverhead();
} }
/** /// @notice Retrieves the current fee scalar.
* @notice Retrieves the current fee scalar. /// @return Current fee scalar.
*
* @return Current fee scalar.
*/
function scalar() public view returns (uint256) { function scalar() public view returns (uint256) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeScalar(); return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeScalar();
} }
/** /// @notice Retrieves the latest known L1 base fee.
* @notice Retrieves the latest known L1 base fee. /// @return Latest known L1 base fee.
*
* @return Latest known L1 base fee.
*/
function l1BaseFee() public view returns (uint256) { function l1BaseFee() public view returns (uint256) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee(); return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee();
} }
/** /// @custom:legacy
* @custom:legacy /// @notice Retrieves the number of decimals used in the scalar.
* @notice Retrieves the number of decimals used in the scalar. /// @return Number of decimals used in the scalar.
*
* @return Number of decimals used in the scalar.
*/
function decimals() public pure returns (uint256) { function decimals() public pure returns (uint256) {
return DECIMALS; return DECIMALS;
} }
/** /// @notice Computes the amount of L1 gas used for a transaction. Adds the overhead which
* @notice Computes the amount of L1 gas used for a transaction. Adds the overhead which /// represents the per-transaction gas overhead of posting the transaction and state
* represents the per-transaction gas overhead of posting the transaction and state /// roots to L1. Adds 68 bytes of padding to account for the fact that the input does
* roots to L1. Adds 68 bytes of padding to account for the fact that the input does /// not have a signature.
* not have a signature. /// @param _data Unsigned fully RLP-encoded transaction to get the L1 gas for.
* /// @return Amount of L1 gas used to publish the transaction.
* @param _data Unsigned fully RLP-encoded transaction to get the L1 gas for.
*
* @return Amount of L1 gas used to publish the transaction.
*/
function getL1GasUsed(bytes memory _data) public view returns (uint256) { function getL1GasUsed(bytes memory _data) public view returns (uint256) {
uint256 total = 0; uint256 total = 0;
uint256 length = _data.length; uint256 length = _data.length;
......
...@@ -3,79 +3,54 @@ pragma solidity 0.8.15; ...@@ -3,79 +3,54 @@ pragma solidity 0.8.15;
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
/** /// @custom:proxied
* @custom:proxied /// @custom:predeploy 0x4200000000000000000000000000000000000015
* @custom:predeploy 0x4200000000000000000000000000000000000015 /// @title L1Block
* @title L1Block /// @notice The L1Block predeploy gives users access to information about the last known L1 block.
* @notice The L1Block predeploy gives users access to information about the last known L1 block. /// Values within this contract are updated once per epoch (every L1 block) and can only be
* Values within this contract are updated once per epoch (every L1 block) and can only be /// set by the "depositor" account, a special system address. Depositor account transactions
* set by the "depositor" account, a special system address. Depositor account transactions /// are created by the protocol whenever we move to a new epoch.
* are created by the protocol whenever we move to a new epoch.
*/
contract L1Block is Semver { contract L1Block is Semver {
/** /// @notice Address of the special depositor account.
* @notice Address of the special depositor account.
*/
address public constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001; address public constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001;
/** /// @notice The latest L1 block number known by the L2 system.
* @notice The latest L1 block number known by the L2 system.
*/
uint64 public number; uint64 public number;
/** /// @notice The latest L1 timestamp known by the L2 system.
* @notice The latest L1 timestamp known by the L2 system.
*/
uint64 public timestamp; uint64 public timestamp;
/** /// @notice The latest L1 basefee.
* @notice The latest L1 basefee.
*/
uint256 public basefee; uint256 public basefee;
/** /// @notice The latest L1 blockhash.
* @notice The latest L1 blockhash.
*/
bytes32 public hash; bytes32 public hash;
/** /// @notice The number of L2 blocks in the same epoch.
* @notice The number of L2 blocks in the same epoch.
*/
uint64 public sequenceNumber; uint64 public sequenceNumber;
/** /// @notice The versioned hash to authenticate the batcher by.
* @notice The versioned hash to authenticate the batcher by.
*/
bytes32 public batcherHash; bytes32 public batcherHash;
/** /// @notice The overhead value applied to the L1 portion of the transaction fee.
* @notice The overhead value applied to the L1 portion of the transaction
* fee.
*/
uint256 public l1FeeOverhead; uint256 public l1FeeOverhead;
/** /// @notice The scalar value applied to the L1 portion of the transaction fee.
* @notice The scalar value applied to the L1 portion of the transaction fee.
*/
uint256 public l1FeeScalar; uint256 public l1FeeScalar;
/** /// @custom:semver 1.0.1
* @custom:semver 1.0.0 /// @notice Constructs the L1Block contract.
*/ constructor() Semver(1, 0, 1) {}
constructor() Semver(1, 0, 0) {}
/** /// @notice Updates the L1 block values.
* @notice Updates the L1 block values. /// @param _number L1 blocknumber.
* /// @param _timestamp L1 timestamp.
* @param _number L1 blocknumber. /// @param _basefee L1 basefee.
* @param _timestamp L1 timestamp. /// @param _hash L1 blockhash.
* @param _basefee L1 basefee. /// @param _sequenceNumber Number of L2 blocks since epoch start.
* @param _hash L1 blockhash. /// @param _batcherHash Versioned hash to authenticate batcher by.
* @param _sequenceNumber Number of L2 blocks since epoch start. /// @param _l1FeeOverhead L1 fee overhead.
* @param _batcherHash Versioned hash to authenticate batcher by. /// @param _l1FeeScalar L1 fee scalar.
* @param _l1FeeOverhead L1 fee overhead.
* @param _l1FeeScalar L1 fee scalar.
*/
function setL1BlockValues( function setL1BlockValues(
uint64 _number, uint64 _number,
uint64 _timestamp, uint64 _timestamp,
......
...@@ -4,23 +4,19 @@ pragma solidity 0.8.15; ...@@ -4,23 +4,19 @@ pragma solidity 0.8.15;
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
import { FeeVault } from "../universal/FeeVault.sol"; import { FeeVault } from "../universal/FeeVault.sol";
/** /// @custom:proxied
* @custom:proxied /// @custom:predeploy 0x420000000000000000000000000000000000001A
* @custom:predeploy 0x420000000000000000000000000000000000001A /// @title L1FeeVault
* @title L1FeeVault /// @notice The L1FeeVault accumulates the L1 portion of the transaction fees.
* @notice The L1FeeVault accumulates the L1 portion of the transaction fees.
*/
contract L1FeeVault is FeeVault, Semver { contract L1FeeVault is FeeVault, Semver {
/** /// @custom:semver 1.2.1
* @custom:semver 1.2.0 /// @notice Constructs the L1FeeVault contract.
* /// @param _recipient Wallet that will receive the fees.
* @param _recipient Wallet that will receive the fees. /// @param _minWithdrawalAmount Minimum balance for withdrawals.
* @param _minWithdrawalAmount Minimum balance for withdrawals. /// @param _withdrawalNetwork Network which the recipient will receive fees on.
* @param _withdrawalNetwork Network which the recipient will receive fees on.
*/
constructor( constructor(
address _recipient, address _recipient,
uint256 _minWithdrawalAmount, uint256 _minWithdrawalAmount,
WithdrawalNetwork _withdrawalNetwork WithdrawalNetwork _withdrawalNetwork
) FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) Semver(1, 2, 0) {} ) FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) Semver(1, 2, 1) {}
} }
...@@ -7,47 +7,37 @@ import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol"; ...@@ -7,47 +7,37 @@ import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
import { L2ToL1MessagePasser } from "./L2ToL1MessagePasser.sol"; import { L2ToL1MessagePasser } from "./L2ToL1MessagePasser.sol";
/** /// @custom:proxied
* @custom:proxied /// @custom:predeploy 0x4200000000000000000000000000000000000007
* @custom:predeploy 0x4200000000000000000000000000000000000007 /// @title L2CrossDomainMessenger
* @title L2CrossDomainMessenger /// @notice The L2CrossDomainMessenger is a high-level interface for message passing between L1 and
* @notice The L2CrossDomainMessenger is a high-level interface for message passing between L1 and /// L2 on the L2 side. Users are generally encouraged to use this contract instead of lower
* L2 on the L2 side. Users are generally encouraged to use this contract instead of lower /// level message passing contracts.
* level message passing contracts.
*/
contract L2CrossDomainMessenger is CrossDomainMessenger, Semver { contract L2CrossDomainMessenger is CrossDomainMessenger, Semver {
/** /// @custom:semver 1.4.1
* @custom:semver 1.4.0 /// @notice Constructs the L2CrossDomainMessenger contract.
* /// @param _l1CrossDomainMessenger Address of the L1CrossDomainMessenger contract.
* @param _l1CrossDomainMessenger Address of the L1CrossDomainMessenger contract.
*/
constructor(address _l1CrossDomainMessenger) constructor(address _l1CrossDomainMessenger)
Semver(1, 4, 0) Semver(1, 4, 1)
CrossDomainMessenger(_l1CrossDomainMessenger) CrossDomainMessenger(_l1CrossDomainMessenger)
{ {
initialize(); initialize();
} }
/** /// @notice Initializer.
* @notice Initializer.
*/
function initialize() public initializer { function initialize() public initializer {
__CrossDomainMessenger_init(); __CrossDomainMessenger_init();
} }
/** /// @custom:legacy
* @custom:legacy /// @notice Legacy getter for the remote messenger.
* @notice Legacy getter for the remote messenger. Use otherMessenger going forward. /// Use otherMessenger going forward.
* /// @return Address of the L1CrossDomainMessenger contract.
* @return Address of the L1CrossDomainMessenger contract.
*/
function l1CrossDomainMessenger() public view returns (address) { function l1CrossDomainMessenger() public view returns (address) {
return OTHER_MESSENGER; return OTHER_MESSENGER;
} }
/** /// @inheritdoc CrossDomainMessenger
* @inheritdoc CrossDomainMessenger
*/
function _sendMessage( function _sendMessage(
address _to, address _to,
uint64 _gasLimit, uint64 _gasLimit,
...@@ -59,16 +49,12 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver { ...@@ -59,16 +49,12 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver {
}(_to, _gasLimit, _data); }(_to, _gasLimit, _data);
} }
/** /// @inheritdoc CrossDomainMessenger
* @inheritdoc CrossDomainMessenger
*/
function _isOtherMessenger() internal view override returns (bool) { function _isOtherMessenger() internal view override returns (bool) {
return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == OTHER_MESSENGER; return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == OTHER_MESSENGER;
} }
/** /// @inheritdoc CrossDomainMessenger
* @inheritdoc CrossDomainMessenger
*/
function _isUnsafeTarget(address _target) internal view override returns (bool) { function _isUnsafeTarget(address _target) internal view override returns (bool) {
return _target == address(this) || _target == address(Predeploys.L2_TO_L1_MESSAGE_PASSER); return _target == address(this) || _target == address(Predeploys.L2_TO_L1_MESSAGE_PASSER);
} }
......
...@@ -7,42 +7,35 @@ import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol"; ...@@ -7,42 +7,35 @@ import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { IOptimismMintableERC721 } from "../universal/IOptimismMintableERC721.sol"; import { IOptimismMintableERC721 } from "../universal/IOptimismMintableERC721.sol";
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
/** /// @title L2ERC721Bridge
* @title L2ERC721Bridge /// @notice The L2 ERC721 bridge is a contract which works together with the L1 ERC721 bridge to
* @notice The L2 ERC721 bridge is a contract which works together with the L1 ERC721 bridge to /// make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract
* make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract /// acts as a minter for new tokens when it hears about deposits into the L1 ERC721 bridge.
* acts as a minter for new tokens when it hears about deposits into the L1 ERC721 bridge. /// This contract also acts as a burner for tokens being withdrawn.
* This contract also acts as a burner for tokens being withdrawn. /// **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This
* **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This /// bridge ONLY supports ERC721s originally deployed on Ethereum. Users will need to
* bridge ONLY supports ERC721s originally deployed on Ethereum. Users will need to /// wait for the one-week challenge period to elapse before their Optimism-native NFT
* wait for the one-week challenge period to elapse before their Optimism-native NFT /// can be refunded on L2.
* can be refunded on L2.
*/
contract L2ERC721Bridge is ERC721Bridge, Semver { contract L2ERC721Bridge is ERC721Bridge, Semver {
/** /// @custom:semver 1.1.1
* @custom:semver 1.1.0 /// @notice Constructs the L2ERC721Bridge contract.
* /// @param _messenger Address of the CrossDomainMessenger on this network.
* @param _messenger Address of the CrossDomainMessenger on this network. /// @param _otherBridge Address of the ERC721 bridge on the other network.
* @param _otherBridge Address of the ERC721 bridge on the other network.
*/
constructor(address _messenger, address _otherBridge) constructor(address _messenger, address _otherBridge)
Semver(1, 1, 0) Semver(1, 1, 1)
ERC721Bridge(_messenger, _otherBridge) ERC721Bridge(_messenger, _otherBridge)
{} {}
/** /// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
* @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the /// recipient on this domain.
* recipient on this domain. /// @param _localToken Address of the ERC721 token on this domain.
* /// @param _remoteToken Address of the ERC721 token on the other domain.
* @param _localToken Address of the ERC721 token on this domain. /// @param _from Address that triggered the bridge on the other domain.
* @param _remoteToken Address of the ERC721 token on the other domain. /// @param _to Address to receive the token on this domain.
* @param _from Address that triggered the bridge on the other domain. /// @param _tokenId ID of the token being deposited.
* @param _to Address to receive the token on this domain. /// @param _extraData Optional data to forward to L1.
* @param _tokenId ID of the token being deposited. /// Data supplied here will not be used to execute any code on L1 and is
* @param _extraData Optional data to forward to L1. Data supplied here will not be used to /// only emitted as extra data for the convenience of off-chain tooling.
* execute any code on L1 and is only emitted as extra data for the
* convenience of off-chain tooling.
*/
function finalizeBridgeERC721( function finalizeBridgeERC721(
address _localToken, address _localToken,
address _remoteToken, address _remoteToken,
...@@ -73,9 +66,7 @@ contract L2ERC721Bridge is ERC721Bridge, Semver { ...@@ -73,9 +66,7 @@ contract L2ERC721Bridge is ERC721Bridge, Semver {
emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData); emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
} }
/** /// @inheritdoc ERC721Bridge
* @inheritdoc ERC721Bridge
*/
function _initiateBridgeERC721( function _initiateBridgeERC721(
address _localToken, address _localToken,
address _remoteToken, address _remoteToken,
......
...@@ -7,46 +7,33 @@ import { Encoding } from "../libraries/Encoding.sol"; ...@@ -7,46 +7,33 @@ import { Encoding } from "../libraries/Encoding.sol";
import { Burn } from "../libraries/Burn.sol"; import { Burn } from "../libraries/Burn.sol";
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
/** /// @custom:proxied
* @custom:proxied /// @custom:predeploy 0x4200000000000000000000000000000000000016
* @custom:predeploy 0x4200000000000000000000000000000000000016 /// @title L2ToL1MessagePasser
* @title L2ToL1MessagePasser /// @notice The L2ToL1MessagePasser is a dedicated contract where messages that are being sent from
* @notice The L2ToL1MessagePasser is a dedicated contract where messages that are being sent from /// L2 to L1 can be stored. The storage root of this contract is pulled up to the top level
* L2 to L1 can be stored. The storage root of this contract is pulled up to the top level /// of the L2 output to reduce the cost of proving the existence of sent messages.
* of the L2 output to reduce the cost of proving the existence of sent messages.
*/
contract L2ToL1MessagePasser is Semver { contract L2ToL1MessagePasser is Semver {
/** /// @notice The L1 gas limit set when eth is withdrawn using the receive() function.
* @notice The L1 gas limit set when eth is withdrawn using the receive() function.
*/
uint256 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000; uint256 internal constant RECEIVE_DEFAULT_GAS_LIMIT = 100_000;
/** /// @notice The current message version identifier.
* @notice Current message version identifier.
*/
uint16 public constant MESSAGE_VERSION = 1; uint16 public constant MESSAGE_VERSION = 1;
/** /// @notice Includes the message hashes for all withdrawals
* @notice Includes the message hashes for all withdrawals
*/
mapping(bytes32 => bool) public sentMessages; mapping(bytes32 => bool) public sentMessages;
/** /// @notice A unique value hashed with each withdrawal.
* @notice A unique value hashed with each withdrawal.
*/
uint240 internal msgNonce; uint240 internal msgNonce;
/** /// @notice Emitted any time a withdrawal is initiated.
* @notice Emitted any time a withdrawal is initiated. /// @param nonce Unique value corresponding to each withdrawal.
* /// @param sender The L2 account address which initiated the withdrawal.
* @param nonce Unique value corresponding to each withdrawal. /// @param target The L1 account address the call will be send to.
* @param sender The L2 account address which initiated the withdrawal. /// @param value The ETH value submitted for withdrawal, to be forwarded to the target.
* @param target The L1 account address the call will be send to. /// @param gasLimit The minimum amount of gas that must be provided when withdrawing.
* @param value The ETH value submitted for withdrawal, to be forwarded to the target. /// @param data The data to be forwarded to the target on L1.
* @param gasLimit The minimum amount of gas that must be provided when withdrawing. /// @param withdrawalHash The hash of the withdrawal.
* @param data The data to be forwarded to the target on L1.
* @param withdrawalHash The hash of the withdrawal.
*/
event MessagePassed( event MessagePassed(
uint256 indexed nonce, uint256 indexed nonce,
address indexed sender, address indexed sender,
...@@ -57,44 +44,33 @@ contract L2ToL1MessagePasser is Semver { ...@@ -57,44 +44,33 @@ contract L2ToL1MessagePasser is Semver {
bytes32 withdrawalHash bytes32 withdrawalHash
); );
/** /// @notice Emitted when the balance of this contract is burned.
* @notice Emitted when the balance of this contract is burned. /// @param amount Amount of ETh that was burned.
*
* @param amount Amount of ETh that was burned.
*/
event WithdrawerBalanceBurnt(uint256 indexed amount); event WithdrawerBalanceBurnt(uint256 indexed amount);
/** /// @custom:semver 1.0.1
* @custom:semver 1.0.0 /// @notice Constructs the L2ToL1MessagePasser contract.
*/ constructor() Semver(1, 0, 1) {}
constructor() Semver(1, 0, 0) {}
/** /// @notice Allows users to withdraw ETH by sending directly to this contract.
* @notice Allows users to withdraw ETH by sending directly to this contract.
*/
receive() external payable { receive() external payable {
initiateWithdrawal(msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes("")); initiateWithdrawal(msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes(""));
} }
/** /// @notice Removes all ETH held by this contract from the state. Used to prevent the amount of
* @notice Removes all ETH held by this contract from the state. Used to prevent the amount of /// ETH on L2 inflating when ETH is withdrawn. Currently only way to do this is to
* ETH on L2 inflating when ETH is withdrawn. Currently only way to do this is to /// create a contract and self-destruct it to itself. Anyone can call this function. Not
* create a contract and self-destruct it to itself. Anyone can call this function. Not /// incentivized since this function is very cheap.
* incentivized since this function is very cheap.
*/
function burn() external { function burn() external {
uint256 balance = address(this).balance; uint256 balance = address(this).balance;
Burn.eth(balance); Burn.eth(balance);
emit WithdrawerBalanceBurnt(balance); emit WithdrawerBalanceBurnt(balance);
} }
/** /// @notice Sends a message from L2 to L1.
* @notice Sends a message from L2 to L1. /// @param _target Address to call on L1 execution.
* /// @param _gasLimit Minimum gas limit for executing the message on L1.
* @param _target Address to call on L1 execution. /// @param _data Data to forward to L1 target.
* @param _gasLimit Minimum gas limit for executing the message on L1.
* @param _data Data to forward to L1 target.
*/
function initiateWithdrawal( function initiateWithdrawal(
address _target, address _target,
uint256 _gasLimit, uint256 _gasLimit,
...@@ -128,13 +104,10 @@ contract L2ToL1MessagePasser is Semver { ...@@ -128,13 +104,10 @@ contract L2ToL1MessagePasser is Semver {
} }
} }
/** /// @notice Retrieves the next message nonce. Message version will be added to the upper two
* @notice Retrieves the next message nonce. Message version will be added to the upper two /// bytes of the message nonce. Message version allows us to treat messages as having
* bytes of the message nonce. Message version allows us to treat messages as having /// different structures.
* different structures. /// @return Nonce of the next message to be sent, with added message version.
*
* @return Nonce of the next message to be sent, with added message version.
*/
function messageNonce() public view returns (uint256) { function messageNonce() public view returns (uint256) {
return Encoding.encodeVersionedNonce(msgNonce, MESSAGE_VERSION); return Encoding.encodeVersionedNonce(msgNonce, MESSAGE_VERSION);
} }
......
...@@ -4,33 +4,26 @@ pragma solidity 0.8.15; ...@@ -4,33 +4,26 @@ pragma solidity 0.8.15;
import { Semver } from "../universal/Semver.sol"; import { Semver } from "../universal/Semver.sol";
import { FeeVault } from "../universal/FeeVault.sol"; import { FeeVault } from "../universal/FeeVault.sol";
/** /// @custom:proxied
* @custom:proxied /// @custom:predeploy 0x4200000000000000000000000000000000000011
* @custom:predeploy 0x4200000000000000000000000000000000000011 /// @title SequencerFeeVault
* @title SequencerFeeVault /// @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during
* @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during /// transaction processing and block production.
* transaction processing and block production.
*/
contract SequencerFeeVault is FeeVault, Semver { contract SequencerFeeVault is FeeVault, Semver {
/** /// @custom:semver 1.2.1
* @custom:semver 1.2.0 /// @notice Constructs the SequencerFeeVault contract.
* /// @param _recipient Wallet that will receive the fees.
* @param _recipient Wallet that will receive the fees. /// @param _minWithdrawalAmount Minimum balance for withdrawals.
* @param _minWithdrawalAmount Minimum balance for withdrawals. /// @param _withdrawalNetwork Network which the recipient will receive fees on.
* @param _withdrawalNetwork Network which the recipient will receive fees on.
*/
constructor( constructor(
address _recipient, address _recipient,
uint256 _minWithdrawalAmount, uint256 _minWithdrawalAmount,
WithdrawalNetwork _withdrawalNetwork WithdrawalNetwork _withdrawalNetwork
) FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) Semver(1, 2, 0) {} ) FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) Semver(1, 2, 1) {}
/** /// @custom:legacy
* @custom:legacy /// @notice Legacy getter for the recipient address.
* @notice Legacy getter for the recipient address. /// @return The recipient address.
*
* @return The recipient address.
*/
function l1FeeWallet() public view returns (address) { function l1FeeWallet() public view returns (address) {
return RECIPIENT; return RECIPIENT;
} }
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { CommonTest, Portal_Initializer } from "./CommonTest.t.sol"; // Testing utilities
import { CrossDomainOwnable } from "../L2/CrossDomainOwnable.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
import { Vm, VmSafe } from "forge-std/Vm.sol"; import { Vm, VmSafe } from "forge-std/Vm.sol";
import { CommonTest, Portal_Initializer } from "./CommonTest.t.sol";
// Libraries
import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol";
// Target contract dependencies
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
// Target contract
import { CrossDomainOwnable } from "../L2/CrossDomainOwnable.sol";
contract XDomainSetter is CrossDomainOwnable { contract XDomainSetter is CrossDomainOwnable {
uint256 public value; uint256 public value;
...@@ -23,13 +30,13 @@ contract CrossDomainOwnable_Test is CommonTest { ...@@ -23,13 +30,13 @@ contract CrossDomainOwnable_Test is CommonTest {
setter = new XDomainSetter(); setter = new XDomainSetter();
} }
// Check that the revert message is correct /// @dev Tests that the `onlyOwner` modifier reverts with the correct message.
function test_onlyOwner_notOwner_reverts() external { function test_onlyOwner_notOwner_reverts() external {
vm.expectRevert("CrossDomainOwnable: caller is not the owner"); vm.expectRevert("CrossDomainOwnable: caller is not the owner");
setter.set(1); setter.set(1);
} }
// Check that making a call can set the value properly /// @dev Tests that the `onlyOwner` modifier succeeds when called by the owner.
function test_onlyOwner_succeeds() external { function test_onlyOwner_succeeds() external {
assertEq(setter.value(), 0); assertEq(setter.value(), 0);
...@@ -42,6 +49,7 @@ contract CrossDomainOwnable_Test is CommonTest { ...@@ -42,6 +49,7 @@ contract CrossDomainOwnable_Test is CommonTest {
contract CrossDomainOwnableThroughPortal_Test is Portal_Initializer { contract CrossDomainOwnableThroughPortal_Test is Portal_Initializer {
XDomainSetter setter; XDomainSetter setter;
/// @dev Sets up the test suite.
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
...@@ -49,6 +57,8 @@ contract CrossDomainOwnableThroughPortal_Test is Portal_Initializer { ...@@ -49,6 +57,8 @@ contract CrossDomainOwnableThroughPortal_Test is Portal_Initializer {
setter = new XDomainSetter(); setter = new XDomainSetter();
} }
/// @dev Tests that `depositTransaction` succeeds when calling the `set` function on the
/// `XDomainSetter` contract.
function test_depositTransaction_crossDomainOwner_succeeds() external { function test_depositTransaction_crossDomainOwner_succeeds() external {
vm.recordLogs(); vm.recordLogs();
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; // Testing utilities
import { CommonTest, Messenger_Initializer } from "./CommonTest.t.sol"; import { CommonTest, Messenger_Initializer } from "./CommonTest.t.sol";
import { CrossDomainOwnable2 } from "../L2/CrossDomainOwnable2.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; // Libraries
import { Hashing } from "../libraries/Hashing.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.sol"; import { Encoding } from "../libraries/Encoding.sol";
import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol";
// Target contract dependencies
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
// Target contract
import { CrossDomainOwnable2 } from "../L2/CrossDomainOwnable2.sol";
contract XDomainSetter2 is CrossDomainOwnable2 { contract XDomainSetter2 is CrossDomainOwnable2 {
uint256 public value; uint256 public value;
...@@ -19,17 +26,20 @@ contract XDomainSetter2 is CrossDomainOwnable2 { ...@@ -19,17 +26,20 @@ contract XDomainSetter2 is CrossDomainOwnable2 {
contract CrossDomainOwnable2_Test is Messenger_Initializer { contract CrossDomainOwnable2_Test is Messenger_Initializer {
XDomainSetter2 setter; XDomainSetter2 setter;
/// @dev Sets up the test suite.
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
vm.prank(alice); vm.prank(alice);
setter = new XDomainSetter2(); setter = new XDomainSetter2();
} }
/// @dev Tests that the `onlyOwner` modifier reverts when the caller is not the messenger.
function test_onlyOwner_notMessenger_reverts() external { function test_onlyOwner_notMessenger_reverts() external {
vm.expectRevert("CrossDomainOwnable2: caller is not the messenger"); vm.expectRevert("CrossDomainOwnable2: caller is not the messenger");
setter.set(1); setter.set(1);
} }
/// @dev Tests that the `onlyOwner` modifier reverts when not called by the owner.
function test_onlyOwner_notOwner_reverts() external { function test_onlyOwner_notOwner_reverts() external {
// set the xDomainMsgSender storage slot // set the xDomainMsgSender storage slot
bytes32 key = bytes32(uint256(204)); bytes32 key = bytes32(uint256(204));
...@@ -41,6 +51,7 @@ contract CrossDomainOwnable2_Test is Messenger_Initializer { ...@@ -41,6 +51,7 @@ contract CrossDomainOwnable2_Test is Messenger_Initializer {
setter.set(1); setter.set(1);
} }
/// @dev Tests that the `onlyOwner` modifier causes the relayed message to fail.
function test_onlyOwner_notOwner2_reverts() external { function test_onlyOwner_notOwner2_reverts() external {
uint240 nonce = 0; uint240 nonce = 0;
address sender = bob; address sender = bob;
...@@ -76,6 +87,7 @@ contract CrossDomainOwnable2_Test is Messenger_Initializer { ...@@ -76,6 +87,7 @@ contract CrossDomainOwnable2_Test is Messenger_Initializer {
assertEq(setter.value(), 0); assertEq(setter.value(), 0);
} }
/// @dev Tests that the `onlyOwner` modifier succeeds when called by the messenger.
function test_onlyOwner_succeeds() external { function test_onlyOwner_succeeds() external {
address owner = setter.owner(); address owner = setter.owner();
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; // Testing utilities
import { CommonTest, Messenger_Initializer } from "./CommonTest.t.sol"; import { CommonTest, Messenger_Initializer } from "./CommonTest.t.sol";
import { CrossDomainOwnable3 } from "../L2/CrossDomainOwnable3.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; // Libraries
import { Hashing } from "../libraries/Hashing.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.sol"; import { Encoding } from "../libraries/Encoding.sol";
import { Bytes32AddressLib } from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol";
// Target contract dependencies
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
// Target contract
import { CrossDomainOwnable3 } from "../L2/CrossDomainOwnable3.sol";
contract XDomainSetter3 is CrossDomainOwnable3 { contract XDomainSetter3 is CrossDomainOwnable3 {
uint256 public value; uint256 public value;
...@@ -19,43 +26,45 @@ contract XDomainSetter3 is CrossDomainOwnable3 { ...@@ -19,43 +26,45 @@ contract XDomainSetter3 is CrossDomainOwnable3 {
contract CrossDomainOwnable3_Test is Messenger_Initializer { contract CrossDomainOwnable3_Test is Messenger_Initializer {
XDomainSetter3 setter; XDomainSetter3 setter;
/** /// @dev OpenZeppelin Ownable.sol transferOwnership event
* @notice OpenZeppelin Ownable.sol transferOwnership event
*/
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/** /// @dev CrossDomainOwnable3.sol transferOwnership event
* @notice CrossDomainOwnable3.sol transferOwnership event
*/
event OwnershipTransferred( event OwnershipTransferred(
address indexed previousOwner, address indexed previousOwner,
address indexed newOwner, address indexed newOwner,
bool isLocal bool isLocal
); );
/// @dev Sets up the test suite.
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
vm.prank(alice); vm.prank(alice);
setter = new XDomainSetter3(); setter = new XDomainSetter3();
} }
/// @dev Tests that the constructor sets the correct variables.
function test_constructor_succeeds() public { function test_constructor_succeeds() public {
assertEq(setter.owner(), alice); assertEq(setter.owner(), alice);
assertEq(setter.isLocal(), true); assertEq(setter.isLocal(), true);
} }
/// @dev Tests that `set` reverts when the caller is not the owner.
function test_localOnlyOwner_notOwner_reverts() public { function test_localOnlyOwner_notOwner_reverts() public {
vm.prank(bob); vm.prank(bob);
vm.expectRevert("CrossDomainOwnable3: caller is not the owner"); vm.expectRevert("CrossDomainOwnable3: caller is not the owner");
setter.set(1); setter.set(1);
} }
/// @dev Tests that `transferOwnership` reverts when the caller is not the owner.
function test_transferOwnership_notOwner_reverts() public { function test_transferOwnership_notOwner_reverts() public {
vm.prank(bob); vm.prank(bob);
vm.expectRevert("CrossDomainOwnable3: caller is not the owner"); vm.expectRevert("CrossDomainOwnable3: caller is not the owner");
setter.transferOwnership({ _owner: bob, _isLocal: true }); setter.transferOwnership({ _owner: bob, _isLocal: true });
} }
/// @dev Tests that the `XDomainSetter3` contract reverts after the ownership
/// has been transferred to a new owner.
function test_crossDomainOnlyOwner_notOwner_reverts() public { function test_crossDomainOnlyOwner_notOwner_reverts() public {
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
...@@ -78,6 +87,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer { ...@@ -78,6 +87,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
setter.set(1); setter.set(1);
} }
/// @dev Tests that a relayed message to the `XDomainSetter3` contract reverts
/// after its ownership has been transferred to a new owner.
function test_crossDomainOnlyOwner_notOwner2_reverts() public { function test_crossDomainOnlyOwner_notOwner2_reverts() public {
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
...@@ -126,6 +137,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer { ...@@ -126,6 +137,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
assertEq(setter.value(), 0); assertEq(setter.value(), 0);
} }
/// @dev Tests that the `XDomainSetter3` contract reverts for a non-messenger
/// caller after the ownership has been transferred to a new owner.
function test_crossDomainOnlyOwner_notMessenger_reverts() public { function test_crossDomainOnlyOwner_notMessenger_reverts() public {
vm.expectEmit(true, true, true, true); vm.expectEmit(true, true, true, true);
...@@ -143,18 +156,24 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer { ...@@ -143,18 +156,24 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
setter.set(1); setter.set(1);
} }
/// @dev Tests that `transferOwnership` reverts for ownership transfers
/// to the zero address when set locally.
function test_transferOwnership_zeroAddress_reverts() public { function test_transferOwnership_zeroAddress_reverts() public {
vm.prank(setter.owner()); vm.prank(setter.owner());
vm.expectRevert("CrossDomainOwnable3: new owner is the zero address"); vm.expectRevert("CrossDomainOwnable3: new owner is the zero address");
setter.transferOwnership({ _owner: address(0), _isLocal: true }); setter.transferOwnership({ _owner: address(0), _isLocal: true });
} }
/// @dev Tests that `transferOwnership` reverts for ownership transfers
/// to the zero address.
function test_transferOwnership_noLocalZeroAddress_reverts() public { function test_transferOwnership_noLocalZeroAddress_reverts() public {
vm.prank(setter.owner()); vm.prank(setter.owner());
vm.expectRevert("Ownable: new owner is the zero address"); vm.expectRevert("Ownable: new owner is the zero address");
setter.transferOwnership(address(0)); setter.transferOwnership(address(0));
} }
/// @dev Tests that `onlyOwner` allows the owner to call a protected
/// function locally.
function test_localOnlyOwner_succeeds() public { function test_localOnlyOwner_succeeds() public {
assertEq(setter.isLocal(), true); assertEq(setter.isLocal(), true);
vm.prank(setter.owner()); vm.prank(setter.owner());
...@@ -162,6 +181,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer { ...@@ -162,6 +181,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
assertEq(setter.value(), 1); assertEq(setter.value(), 1);
} }
/// @dev Tests that `transferOwnership` succeeds when the caller is the
/// owner and the ownership is transferred locally.
function test_localTransferOwnership_succeeds() public { function test_localTransferOwnership_succeeds() public {
vm.expectEmit(true, true, true, true, address(setter)); vm.expectEmit(true, true, true, true, address(setter));
emit OwnershipTransferred(alice, bob); emit OwnershipTransferred(alice, bob);
...@@ -177,10 +198,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer { ...@@ -177,10 +198,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
assertEq(setter.value(), 2); assertEq(setter.value(), 2);
} }
/** /// @dev The existing transferOwnership(address) method still
* @notice The existing transferOwnership(address) method /// exists on the contract.
* still exists on the contract
*/
function test_transferOwnershipNoLocal_succeeds() public { function test_transferOwnershipNoLocal_succeeds() public {
bool isLocal = setter.isLocal(); bool isLocal = setter.isLocal();
...@@ -198,6 +217,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer { ...@@ -198,6 +217,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
assertEq(setter.value(), 2); assertEq(setter.value(), 2);
} }
/// @dev Tests that `transferOwnership` succeeds when the caller is the
/// owner and the ownership is transferred non-locally.
function test_crossDomainTransferOwnership_succeeds() public { function test_crossDomainTransferOwnership_succeeds() public {
vm.expectEmit(true, true, true, true, address(setter)); vm.expectEmit(true, true, true, true, address(setter));
emit OwnershipTransferred(alice, bob); emit OwnershipTransferred(alice, bob);
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities
import { Bridge_Initializer } from "./CommonTest.t.sol"; import { Bridge_Initializer } from "./CommonTest.t.sol";
import { FeeVault } from "../universal/FeeVault.sol";
import { L1FeeVault } from "../L2/L1FeeVault.sol";
import { BaseFeeVault } from "../L2/BaseFeeVault.sol"; import { BaseFeeVault } from "../L2/BaseFeeVault.sol";
import { StandardBridge } from "../universal/StandardBridge.sol"; import { StandardBridge } from "../universal/StandardBridge.sol";
// Libraries
import { Predeploys } from "../libraries/Predeploys.sol"; import { Predeploys } from "../libraries/Predeploys.sol";
// Target contract dependencies
import { FeeVault } from "../universal/FeeVault.sol";
// Target contract
import { L1FeeVault } from "../L2/L1FeeVault.sol";
// Test the implementations of the FeeVault // Test the implementations of the FeeVault
contract FeeVault_Test is Bridge_Initializer { contract FeeVault_Test is Bridge_Initializer {
BaseFeeVault baseFeeVault = BaseFeeVault(payable(Predeploys.BASE_FEE_VAULT)); BaseFeeVault baseFeeVault = BaseFeeVault(payable(Predeploys.BASE_FEE_VAULT));
...@@ -16,6 +22,7 @@ contract FeeVault_Test is Bridge_Initializer { ...@@ -16,6 +22,7 @@ contract FeeVault_Test is Bridge_Initializer {
uint256 constant otherMinimumWithdrawalAmount = 10 ether; uint256 constant otherMinimumWithdrawalAmount = 10 ether;
/// @dev Sets up the test suite.
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
vm.etch( vm.etch(
...@@ -33,6 +40,7 @@ contract FeeVault_Test is Bridge_Initializer { ...@@ -33,6 +40,7 @@ contract FeeVault_Test is Bridge_Initializer {
vm.label(Predeploys.L1_FEE_VAULT, "L1FeeVault"); vm.label(Predeploys.L1_FEE_VAULT, "L1FeeVault");
} }
/// @dev Tests that the constructor sets the correct values.
function test_constructor_succeeds() external { function test_constructor_succeeds() external {
assertEq(baseFeeVault.RECIPIENT(), alice); assertEq(baseFeeVault.RECIPIENT(), alice);
assertEq(l1FeeVault.RECIPIENT(), bob); assertEq(l1FeeVault.RECIPIENT(), bob);
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "./CommonTest.t.sol"; import { CommonTest } from "./CommonTest.t.sol";
import { GasPriceOracle } from "../L2/GasPriceOracle.sol";
// Target contract dependencies
import { L1Block } from "../L2/L1Block.sol"; import { L1Block } from "../L2/L1Block.sol";
import { Predeploys } from "../libraries/Predeploys.sol"; import { Predeploys } from "../libraries/Predeploys.sol";
// Target contract
import { GasPriceOracle } from "../L2/GasPriceOracle.sol";
contract GasPriceOracle_Test is CommonTest { contract GasPriceOracle_Test is CommonTest {
event OverheadUpdated(uint256); event OverheadUpdated(uint256);
event ScalarUpdated(uint256); event ScalarUpdated(uint256);
...@@ -15,7 +20,7 @@ contract GasPriceOracle_Test is CommonTest { ...@@ -15,7 +20,7 @@ contract GasPriceOracle_Test is CommonTest {
L1Block l1Block; L1Block l1Block;
address depositor; address depositor;
// set the initial L1 context values // The initial L1 context values
uint64 constant number = 10; uint64 constant number = 10;
uint64 constant timestamp = 11; uint64 constant timestamp = 11;
uint256 constant basefee = 100; uint256 constant basefee = 100;
...@@ -25,6 +30,7 @@ contract GasPriceOracle_Test is CommonTest { ...@@ -25,6 +30,7 @@ contract GasPriceOracle_Test is CommonTest {
uint256 constant l1FeeOverhead = 310; uint256 constant l1FeeOverhead = 310;
uint256 constant l1FeeScalar = 10; uint256 constant l1FeeScalar = 10;
/// @dev Sets up the test suite.
function setUp() public virtual override { function setUp() public virtual override {
super.setUp(); super.setUp();
// place the L1Block contract at the predeploy address // place the L1Block contract at the predeploy address
...@@ -51,36 +57,42 @@ contract GasPriceOracle_Test is CommonTest { ...@@ -51,36 +57,42 @@ contract GasPriceOracle_Test is CommonTest {
}); });
} }
/// @dev Tests that `l1BaseFee` is set correctly.
function test_l1BaseFee_succeeds() external { function test_l1BaseFee_succeeds() external {
assertEq(gasOracle.l1BaseFee(), basefee); assertEq(gasOracle.l1BaseFee(), basefee);
} }
/// @dev Tests that `gasPrice` is set correctly.
function test_gasPrice_succeeds() external { function test_gasPrice_succeeds() external {
vm.fee(100); vm.fee(100);
uint256 gasPrice = gasOracle.gasPrice(); uint256 gasPrice = gasOracle.gasPrice();
assertEq(gasPrice, 100); assertEq(gasPrice, 100);
} }
/// @dev Tests that `baseFee` is set correctly.
function test_baseFee_succeeds() external { function test_baseFee_succeeds() external {
vm.fee(64); vm.fee(64);
uint256 gasPrice = gasOracle.baseFee(); uint256 gasPrice = gasOracle.baseFee();
assertEq(gasPrice, 64); assertEq(gasPrice, 64);
} }
/// @dev Tests that `scalar` is set correctly.
function test_scalar_succeeds() external { function test_scalar_succeeds() external {
assertEq(gasOracle.scalar(), l1FeeScalar); assertEq(gasOracle.scalar(), l1FeeScalar);
} }
/// @dev Tests that `overhead` is set correctly.
function test_overhead_succeeds() external { function test_overhead_succeeds() external {
assertEq(gasOracle.overhead(), l1FeeOverhead); assertEq(gasOracle.overhead(), l1FeeOverhead);
} }
/// @dev Tests that `decimals` is set correctly.
function test_decimals_succeeds() external { function test_decimals_succeeds() external {
assertEq(gasOracle.decimals(), 6); assertEq(gasOracle.decimals(), 6);
assertEq(gasOracle.DECIMALS(), 6); assertEq(gasOracle.DECIMALS(), 6);
} }
// Removed in bedrock /// @dev Tests that `setGasPrice` reverts since it was removed in bedrock.
function test_setGasPrice_doesNotExist_reverts() external { function test_setGasPrice_doesNotExist_reverts() external {
(bool success, bytes memory returndata) = address(gasOracle).call( (bool success, bytes memory returndata) = address(gasOracle).call(
abi.encodeWithSignature("setGasPrice(uint256)", 1) abi.encodeWithSignature("setGasPrice(uint256)", 1)
...@@ -90,7 +102,7 @@ contract GasPriceOracle_Test is CommonTest { ...@@ -90,7 +102,7 @@ contract GasPriceOracle_Test is CommonTest {
assertEq(returndata, hex""); assertEq(returndata, hex"");
} }
// Removed in bedrock /// @dev Tests that `setL1BaseFee` reverts since it was removed in bedrock.
function test_setL1BaseFee_doesNotExist_reverts() external { function test_setL1BaseFee_doesNotExist_reverts() external {
(bool success, bytes memory returndata) = address(gasOracle).call( (bool success, bytes memory returndata) = address(gasOracle).call(
abi.encodeWithSignature("setL1BaseFee(uint256)", 1) abi.encodeWithSignature("setL1BaseFee(uint256)", 1)
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "./CommonTest.t.sol"; import { CommonTest } from "./CommonTest.t.sol";
// Target contract
import { L1Block } from "../L2/L1Block.sol"; import { L1Block } from "../L2/L1Block.sol";
contract L1BlockTest is CommonTest { contract L1BlockTest is CommonTest {
...@@ -9,6 +12,7 @@ contract L1BlockTest is CommonTest { ...@@ -9,6 +12,7 @@ contract L1BlockTest is CommonTest {
address depositor; address depositor;
bytes32 immutable NON_ZERO_HASH = keccak256(abi.encode(1)); bytes32 immutable NON_ZERO_HASH = keccak256(abi.encode(1));
/// @dev Sets up the test suite.
function setUp() public virtual override { function setUp() public virtual override {
super.setUp(); super.setUp();
lb = new L1Block(); lb = new L1Block();
...@@ -26,6 +30,7 @@ contract L1BlockTest is CommonTest { ...@@ -26,6 +30,7 @@ contract L1BlockTest is CommonTest {
}); });
} }
/// @dev Tests that `setL1BlockValues` updates the values correctly.
function testFuzz_updatesValues_succeeds( function testFuzz_updatesValues_succeeds(
uint64 n, uint64 n,
uint64 t, uint64 t,
...@@ -48,26 +53,32 @@ contract L1BlockTest is CommonTest { ...@@ -48,26 +53,32 @@ contract L1BlockTest is CommonTest {
assertEq(lb.l1FeeScalar(), fs); assertEq(lb.l1FeeScalar(), fs);
} }
/// @dev Tests that `number` returns the correct value.
function test_number_succeeds() external { function test_number_succeeds() external {
assertEq(lb.number(), uint64(1)); assertEq(lb.number(), uint64(1));
} }
/// @dev Tests that `timestamp` returns the correct value.
function test_timestamp_succeeds() external { function test_timestamp_succeeds() external {
assertEq(lb.timestamp(), uint64(2)); assertEq(lb.timestamp(), uint64(2));
} }
/// @dev Tests that `basefee` returns the correct value.
function test_basefee_succeeds() external { function test_basefee_succeeds() external {
assertEq(lb.basefee(), 3); assertEq(lb.basefee(), 3);
} }
/// @dev Tests that `hash` returns the correct value.
function test_hash_succeeds() external { function test_hash_succeeds() external {
assertEq(lb.hash(), NON_ZERO_HASH); assertEq(lb.hash(), NON_ZERO_HASH);
} }
/// @dev Tests that `sequenceNumber` returns the correct value.
function test_sequenceNumber_succeeds() external { function test_sequenceNumber_succeeds() external {
assertEq(lb.sequenceNumber(), uint64(4)); assertEq(lb.sequenceNumber(), uint64(4));
} }
/// @dev Tests that `setL1BlockValues` can set max values.
function test_updateValues_succeeds() external { function test_updateValues_succeeds() external {
vm.prank(depositor); vm.prank(depositor);
lb.setL1BlockValues({ lb.setL1BlockValues({
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities
import { Messenger_Initializer, Reverter, ConfigurableCaller } from "./CommonTest.t.sol"; import { Messenger_Initializer, Reverter, ConfigurableCaller } from "./CommonTest.t.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol"; // Libraries
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { Hashing } from "../libraries/Hashing.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.sol"; import { Encoding } from "../libraries/Encoding.sol";
import { Types } from "../libraries/Types.sol"; import { Types } from "../libraries/Types.sol";
// Target contract dependencies
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
// Target contract
import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol";
contract L2CrossDomainMessenger_Test is Messenger_Initializer { contract L2CrossDomainMessenger_Test is Messenger_Initializer {
// Receiver address for testing /// @dev Receiver address for testing
address recipient = address(0xabbaacdc); address recipient = address(0xabbaacdc);
/// @dev Tests that `messageNonce` can be decoded correctly.
function test_messageVersion_succeeds() external { function test_messageVersion_succeeds() external {
(, uint16 version) = Encoding.decodeVersionedNonce(L2Messenger.messageNonce()); (, uint16 version) = Encoding.decodeVersionedNonce(L2Messenger.messageNonce());
assertEq(version, L2Messenger.MESSAGE_VERSION()); assertEq(version, L2Messenger.MESSAGE_VERSION());
} }
/// @dev Tests that `sendMessage` executes successfully.
function test_sendMessage_succeeds() external { function test_sendMessage_succeeds() external {
bytes memory xDomainCallData = Encoding.encodeCrossDomainMessage( bytes memory xDomainCallData = Encoding.encodeCrossDomainMessage(
L2Messenger.messageNonce(), L2Messenger.messageNonce(),
...@@ -65,6 +72,8 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -65,6 +72,8 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
L2Messenger.sendMessage(recipient, hex"ff", uint32(100)); L2Messenger.sendMessage(recipient, hex"ff", uint32(100));
} }
/// @dev Tests that `sendMessage` can be called twice and that
/// the nonce increments correctly.
function test_sendMessage_twice_succeeds() external { function test_sendMessage_twice_succeeds() external {
uint256 nonce = L2Messenger.messageNonce(); uint256 nonce = L2Messenger.messageNonce();
L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000)); L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
...@@ -73,11 +82,13 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -73,11 +82,13 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
assertEq(nonce + 2, L2Messenger.messageNonce()); assertEq(nonce + 2, L2Messenger.messageNonce());
} }
/// @dev Tests that `sendMessage` reverts if the recipient is the zero address.
function test_xDomainSender_senderNotSet_reverts() external { function test_xDomainSender_senderNotSet_reverts() external {
vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set"); vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender(); L2Messenger.xDomainMessageSender();
} }
/// @dev Tests that `sendMessage` reverts if the message version is not supported.
function test_relayMessage_v2_reverts() external { function test_relayMessage_v2_reverts() external {
address target = address(0xabcd); address target = address(0xabcd);
address sender = address(L1Messenger); address sender = address(L1Messenger);
...@@ -100,6 +111,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -100,6 +111,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
); );
} }
/// @dev Tests that `relayMessage` executes successfully.
function test_relayMessage_succeeds() external { function test_relayMessage_succeeds() external {
address target = address(0xabcd); address target = address(0xabcd);
address sender = address(L1Messenger); address sender = address(L1Messenger);
...@@ -137,7 +149,8 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -137,7 +149,8 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
assertEq(L2Messenger.failedMessages(hash), false); assertEq(L2Messenger.failedMessages(hash), false);
} }
// relayMessage: should revert if attempting to relay a message sent to an L1 system contract /// @dev Tests that `relayMessage` reverts if attempting to relay
/// a message sent to an L1 system contract.
function test_relayMessage_toSystemContract_reverts() external { function test_relayMessage_toSystemContract_reverts() external {
address target = address(messagePasser); address target = address(messagePasser);
address sender = address(L1Messenger); address sender = address(L1Messenger);
...@@ -156,7 +169,8 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -156,7 +169,8 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
); );
} }
// relayMessage: the xDomainMessageSender is reset to the original value /// @dev Tests that `relayMessage` correctly resets the `xDomainMessageSender`
/// to the original value after a message is relayed.
function test_xDomainMessageSender_reset_succeeds() external { function test_xDomainMessageSender_reset_succeeds() external {
vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set"); vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender(); L2Messenger.xDomainMessageSender();
...@@ -176,8 +190,9 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer { ...@@ -176,8 +190,9 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
L2Messenger.xDomainMessageSender(); L2Messenger.xDomainMessageSender();
} }
// relayMessage: should send a successful call to the target contract after the first message /// @dev Tests that `relayMessage` is able to send a successful call
// fails and ETH gets stuck, but the second message succeeds /// to the target contract after the first message fails and ETH
/// gets stuck, but the second message succeeds.
function test_relayMessage_retry_succeeds() external { function test_relayMessage_retry_succeeds() external {
address target = address(0xabcd); address target = address(0xabcd);
address sender = address(L1Messenger); address sender = address(L1Messenger);
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; // Testing utilities
import { Messenger_Initializer } from "./CommonTest.t.sol"; import { Messenger_Initializer } from "./CommonTest.t.sol";
// Target contract dependencies
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol"; import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { L2ERC721Bridge } from "../L2/L2ERC721Bridge.sol";
import { OptimismMintableERC721 } from "../universal/OptimismMintableERC721.sol"; import { OptimismMintableERC721 } from "../universal/OptimismMintableERC721.sol";
// Target contract
import { L2ERC721Bridge } from "../L2/L2ERC721Bridge.sol";
contract TestERC721 is ERC721 { contract TestERC721 is ERC721 {
constructor() ERC721("Test", "TST") {} constructor() ERC721("Test", "TST") {}
...@@ -50,6 +55,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -50,6 +55,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
bytes extraData bytes extraData
); );
/// @dev Sets up the test suite.
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
...@@ -69,6 +75,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -69,6 +75,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
localToken.approve(address(bridge), tokenId); localToken.approve(address(bridge), tokenId);
} }
/// @dev Tests that the constructor sets the correct variables.
function test_constructor_succeeds() public { function test_constructor_succeeds() public {
assertEq(address(bridge.MESSENGER()), address(L2Messenger)); assertEq(address(bridge.MESSENGER()), address(L2Messenger));
assertEq(address(bridge.OTHER_BRIDGE()), otherBridge); assertEq(address(bridge.OTHER_BRIDGE()), otherBridge);
...@@ -76,6 +83,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -76,6 +83,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(address(bridge.otherBridge()), otherBridge); assertEq(address(bridge.otherBridge()), otherBridge);
} }
/// @dev Tests that `bridgeERC721` correctly bridges a token and
/// burns it on the origin chain.
function test_bridgeERC721_succeeds() public { function test_bridgeERC721_succeeds() public {
// Expect a call to the messenger. // Expect a call to the messenger.
vm.expectCall( vm.expectCall(
...@@ -120,6 +129,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -120,6 +129,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
localToken.ownerOf(tokenId); localToken.ownerOf(tokenId);
} }
/// @dev Tests that `bridgeERC721` reverts if the owner is not an EOA.
function test_bridgeERC721_fromContract_reverts() external { function test_bridgeERC721_fromContract_reverts() external {
// Bridge the token. // Bridge the token.
vm.etch(alice, hex"01"); vm.etch(alice, hex"01");
...@@ -131,6 +141,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -131,6 +141,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice); assertEq(localToken.ownerOf(tokenId), alice);
} }
/// @dev Tests that `bridgeERC721` reverts if the local token is the zero address.
function test_bridgeERC721_localTokenZeroAddress_reverts() external { function test_bridgeERC721_localTokenZeroAddress_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
...@@ -141,6 +152,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -141,6 +152,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice); assertEq(localToken.ownerOf(tokenId), alice);
} }
/// @dev Tests that `bridgeERC721` reverts if the remote token is the zero address.
function test_bridgeERC721_remoteTokenZeroAddress_reverts() external { function test_bridgeERC721_remoteTokenZeroAddress_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
...@@ -151,6 +163,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -151,6 +163,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice); assertEq(localToken.ownerOf(tokenId), alice);
} }
/// @dev Tests that `bridgeERC721` reverts if the caller is not the token owner.
function test_bridgeERC721_wrongOwner_reverts() external { function test_bridgeERC721_wrongOwner_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(bob); vm.prank(bob);
...@@ -161,6 +174,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -161,6 +174,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice); assertEq(localToken.ownerOf(tokenId), alice);
} }
/// @dev Tests that `bridgeERC721To` correctly bridges a token
/// and burns it on the origin chain.
function test_bridgeERC721To_succeeds() external { function test_bridgeERC721To_succeeds() external {
// Expect a call to the messenger. // Expect a call to the messenger.
vm.expectCall( vm.expectCall(
...@@ -205,6 +220,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -205,6 +220,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
localToken.ownerOf(tokenId); localToken.ownerOf(tokenId);
} }
/// @dev Tests that `bridgeERC721To` reverts if the local token is the zero address.
function test_bridgeERC721To_localTokenZeroAddress_reverts() external { function test_bridgeERC721To_localTokenZeroAddress_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
...@@ -215,6 +231,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -215,6 +231,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice); assertEq(localToken.ownerOf(tokenId), alice);
} }
/// @dev Tests that `bridgeERC721To` reverts if the remote token is the zero address.
function test_bridgeERC721To_remoteTokenZeroAddress_reverts() external { function test_bridgeERC721To_remoteTokenZeroAddress_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
...@@ -225,6 +242,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -225,6 +242,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice); assertEq(localToken.ownerOf(tokenId), alice);
} }
/// @dev Tests that `bridgeERC721To` reverts if the caller is not the token owner.
function test_bridgeERC721To_wrongOwner_reverts() external { function test_bridgeERC721To_wrongOwner_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(bob); vm.prank(bob);
...@@ -242,6 +260,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -242,6 +260,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice); assertEq(localToken.ownerOf(tokenId), alice);
} }
/// @dev Tests that `finalizeBridgeERC721` correctly finalizes a bridged token.
function test_finalizeBridgeERC721_succeeds() external { function test_finalizeBridgeERC721_succeeds() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
...@@ -278,6 +297,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -278,6 +297,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice); assertEq(localToken.ownerOf(tokenId), alice);
} }
/// @dev Tests that `finalizeBridgeERC721` reverts if the token is not compliant
/// with the `IOptimismMintableERC721` interface.
function test_finalizeBridgeERC721_interfaceNotCompliant_reverts() external { function test_finalizeBridgeERC721_interfaceNotCompliant_reverts() external {
// Create a non-compliant token // Create a non-compliant token
NonCompliantERC721 nonCompliantToken = new NonCompliantERC721(alice); NonCompliantERC721 nonCompliantToken = new NonCompliantERC721(alice);
...@@ -305,6 +326,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -305,6 +326,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
); );
} }
/// @dev Tests that `finalizeBridgeERC721` reverts when not called by the remote bridge.
function test_finalizeBridgeERC721_notViaLocalMessenger_reverts() external { function test_finalizeBridgeERC721_notViaLocalMessenger_reverts() external {
// Finalize a withdrawal. // Finalize a withdrawal.
vm.prank(alice); vm.prank(alice);
...@@ -319,6 +341,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -319,6 +341,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
); );
} }
/// @dev Tests that `finalizeBridgeERC721` reverts when not called by the remote bridge.
function test_finalizeBridgeERC721_notFromRemoteMessenger_reverts() external { function test_finalizeBridgeERC721_notFromRemoteMessenger_reverts() external {
// Finalize a withdrawal. // Finalize a withdrawal.
vm.mockCall( vm.mockCall(
...@@ -338,6 +361,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -338,6 +361,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
); );
} }
/// @dev Tests that `finalizeBridgeERC721` reverts when the local token is the
/// address of the bridge itself.
function test_finalizeBridgeERC721_selfToken_reverts() external { function test_finalizeBridgeERC721_selfToken_reverts() external {
// Finalize a withdrawal. // Finalize a withdrawal.
vm.mockCall( vm.mockCall(
...@@ -357,6 +382,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -357,6 +382,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
); );
} }
/// @dev Tests that `finalizeBridgeERC721` reverts when already finalized.
function test_finalizeBridgeERC721_alreadyExists_reverts() external { function test_finalizeBridgeERC721_alreadyExists_reverts() external {
// Finalize a withdrawal. // Finalize a withdrawal.
vm.mockCall( vm.mockCall(
...@@ -377,12 +403,9 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -377,12 +403,9 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
} }
} }
/** /// @dev A non-compliant ERC721 token that does not implement the full ERC721 interface.
* @dev A non-compliant ERC721 token that does not implement the full ERC721 interface. /// This is used to test that the bridge will revert if the token does not claim to
* /// support the ERC721 interface.
* This is used to test that the bridge will revert if the token does not claim to support
* the ERC721 interface.
*/
contract NonCompliantERC721 { contract NonCompliantERC721 {
address internal immutable owner; address internal immutable owner;
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities
// Target contract is imported by the `Bridge_Initializer`
import { Bridge_Initializer } from "./CommonTest.t.sol"; import { Bridge_Initializer } from "./CommonTest.t.sol";
import { stdStorage, StdStorage } from "forge-std/Test.sol"; import { stdStorage, StdStorage } from "forge-std/Test.sol";
import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol"; import { CrossDomainMessenger } from "../universal/CrossDomainMessenger.sol";
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
import { Predeploys } from "../libraries/Predeploys.sol";
import { console } from "forge-std/console.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol"; import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// Libraries
import { Hashing } from "../libraries/Hashing.sol"; import { Hashing } from "../libraries/Hashing.sol";
import { Types } from "../libraries/Types.sol"; import { Types } from "../libraries/Types.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// Target contract dependencies
import { Predeploys } from "../libraries/Predeploys.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol"; import { OptimismMintableERC20 } from "../universal/OptimismMintableERC20.sol";
contract L2StandardBridge_Test is Bridge_Initializer { contract L2StandardBridge_Test is Bridge_Initializer {
using stdStorage for StdStorage; using stdStorage for StdStorage;
/// @dev Tests that the bridge is initialized correctly.
function test_initialize_succeeds() external { function test_initialize_succeeds() external {
assertEq(address(L2Bridge.messenger()), address(L2Messenger)); assertEq(address(L2Bridge.messenger()), address(L2Messenger));
assertEq(L1Bridge.l2TokenBridge(), address(L2Bridge)); assertEq(L1Bridge.l2TokenBridge(), address(L2Bridge));
assertEq(address(L2Bridge.OTHER_BRIDGE()), address(L1Bridge)); assertEq(address(L2Bridge.OTHER_BRIDGE()), address(L1Bridge));
} }
// receive /// @dev Tests that the bridge receives ETH and successfully initiates a withdrawal.
// - can accept ETH
function test_receive_succeeds() external { function test_receive_succeeds() external {
assertEq(address(messagePasser).balance, 0); assertEq(address(messagePasser).balance, 0);
uint256 nonce = L2Messenger.messageNonce(); uint256 nonce = L2Messenger.messageNonce();
...@@ -109,8 +113,7 @@ contract L2StandardBridge_Test is Bridge_Initializer { ...@@ -109,8 +113,7 @@ contract L2StandardBridge_Test is Bridge_Initializer {
assertEq(address(messagePasser).balance, 100); assertEq(address(messagePasser).balance, 100);
} }
// withrdraw /// @dev Tests that `withdraw` reverts if the amount is not equal to the value sent.
// - requires amount == msg.value
function test_withdraw_insufficientValue_reverts() external { function test_withdraw_insufficientValue_reverts() external {
assertEq(address(messagePasser).balance, 0); assertEq(address(messagePasser).balance, 0);
...@@ -119,10 +122,8 @@ contract L2StandardBridge_Test is Bridge_Initializer { ...@@ -119,10 +122,8 @@ contract L2StandardBridge_Test is Bridge_Initializer {
L2Bridge.withdraw(address(Predeploys.LEGACY_ERC20_ETH), 100, 1000, hex""); L2Bridge.withdraw(address(Predeploys.LEGACY_ERC20_ETH), 100, 1000, hex"");
} }
/** /// @dev Tests that the legacy `withdraw` interface on the L2StandardBridge
* @notice Use the legacy `withdraw` interface on the L2StandardBridge to /// successfully initiates a withdrawal.
* withdraw ether from L2 to L1.
*/
function test_withdraw_ether_succeeds() external { function test_withdraw_ether_succeeds() external {
assertTrue(alice.balance >= 100); assertTrue(alice.balance >= 100);
assertEq(Predeploys.L2_TO_L1_MESSAGE_PASSER.balance, 0); assertEq(Predeploys.L2_TO_L1_MESSAGE_PASSER.balance, 0);
...@@ -153,8 +154,7 @@ contract L2StandardBridge_Test is Bridge_Initializer { ...@@ -153,8 +154,7 @@ contract L2StandardBridge_Test is Bridge_Initializer {
} }
contract PreBridgeERC20 is Bridge_Initializer { contract PreBridgeERC20 is Bridge_Initializer {
// withdraw and BridgeERC20 should behave the same when transferring ERC20 tokens /// @dev Sets up expected calls and emits for a successful ERC20 withdrawal.
// so they should share the same setup and expectEmit calls
function _preBridgeERC20(bool _isLegacy, address _l2Token) internal { function _preBridgeERC20(bool _isLegacy, address _l2Token) internal {
// Alice has 100 L2Token // Alice has 100 L2Token
deal(_l2Token, alice, 100, true); deal(_l2Token, alice, 100, true);
...@@ -430,10 +430,8 @@ contract PreBridgeERC20To is Bridge_Initializer { ...@@ -430,10 +430,8 @@ contract PreBridgeERC20To is Bridge_Initializer {
} }
contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To { contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To {
// withdrawTo /// @dev Tests that `withdrawTo` burns the tokens, emits `WithdrawalInitiated`,
// - token is burned /// and initiates a withdrawal with `Withdrawer.initiateWithdrawal`.
// - emits WithdrawalInitiated w/ correct recipient
// - calls Withdrawer.initiateWithdrawal
function test_withdrawTo_withdrawingERC20_succeeds() external { function test_withdrawTo_withdrawingERC20_succeeds() external {
_preBridgeERC20To({ _isLegacy: true, _l2Token: address(L2Token) }); _preBridgeERC20To({ _isLegacy: true, _l2Token: address(L2Token) });
L2Bridge.withdrawTo(address(L2Token), bob, 100, 1000, hex""); L2Bridge.withdrawTo(address(L2Token), bob, 100, 1000, hex"");
...@@ -441,10 +439,8 @@ contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To { ...@@ -441,10 +439,8 @@ contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To {
assertEq(L2Token.balanceOf(alice), 0); assertEq(L2Token.balanceOf(alice), 0);
} }
// bridgeERC20To /// @dev Tests that `bridgeERC20To` burns the tokens, emits `WithdrawalInitiated`,
// - token is burned /// and initiates a withdrawal with `Withdrawer.initiateWithdrawal`.
// - emits WithdrawalInitiated w/ correct recipient
// - calls Withdrawer.initiateWithdrawal
function test_bridgeERC20To_succeeds() external { function test_bridgeERC20To_succeeds() external {
_preBridgeERC20To({ _isLegacy: false, _l2Token: address(L2Token) }); _preBridgeERC20To({ _isLegacy: false, _l2Token: address(L2Token) });
L2Bridge.bridgeERC20To(address(L2Token), address(L1Token), bob, 100, 1000, hex""); L2Bridge.bridgeERC20To(address(L2Token), address(L1Token), bob, 100, 1000, hex"");
...@@ -453,10 +449,10 @@ contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To { ...@@ -453,10 +449,10 @@ contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To {
} }
contract L2StandardBridge_Bridge_Test is Bridge_Initializer { contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
// finalizeDeposit /// @dev Tests that `finalizeDeposit` succeeds. It should:
// - only callable by l1TokenBridge /// - only be callable by the l1TokenBridge
// - supported token pair emits DepositFinalized /// - emit `DepositFinalized` if the token pair is supported
// - invalid deposit calls Withdrawer.initiateWithdrawal /// - call `Withdrawer.initiateWithdrawal` if the token pair is not supported
function test_finalizeDeposit_depositingERC20_succeeds() external { function test_finalizeDeposit_depositingERC20_succeeds() external {
vm.mockCall( vm.mockCall(
address(L2Bridge.messenger()), address(L2Bridge.messenger()),
...@@ -480,6 +476,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { ...@@ -480,6 +476,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
L2Bridge.finalizeDeposit(address(L1Token), address(L2Token), alice, alice, 100, hex""); L2Bridge.finalizeDeposit(address(L1Token), address(L2Token), alice, alice, 100, hex"");
} }
/// @dev Tests that `finalizeDeposit` succeeds when depositing ETH.
function test_finalizeDeposit_depositingETH_succeeds() external { function test_finalizeDeposit_depositingETH_succeeds() external {
vm.mockCall( vm.mockCall(
address(L2Bridge.messenger()), address(L2Bridge.messenger()),
...@@ -505,6 +502,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { ...@@ -505,6 +502,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
L2Bridge.finalizeDeposit(address(L1Token), address(L2Token), alice, alice, 100, hex""); L2Bridge.finalizeDeposit(address(L1Token), address(L2Token), alice, alice, 100, hex"");
} }
/// @dev Tests that `finalizeDeposit` reverts if the amounts do not match.
function test_finalizeBridgeETH_incorrectValue_reverts() external { function test_finalizeBridgeETH_incorrectValue_reverts() external {
vm.mockCall( vm.mockCall(
address(L2Bridge.messenger()), address(L2Bridge.messenger()),
...@@ -517,6 +515,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { ...@@ -517,6 +515,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
L2Bridge.finalizeBridgeETH{ value: 50 }(alice, alice, 100, hex""); L2Bridge.finalizeBridgeETH{ value: 50 }(alice, alice, 100, hex"");
} }
/// @dev Tests that `finalizeDeposit` reverts if the receipient is the other bridge.
function test_finalizeBridgeETH_sendToSelf_reverts() external { function test_finalizeBridgeETH_sendToSelf_reverts() external {
vm.mockCall( vm.mockCall(
address(L2Bridge.messenger()), address(L2Bridge.messenger()),
...@@ -529,6 +528,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { ...@@ -529,6 +528,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
L2Bridge.finalizeBridgeETH{ value: 100 }(alice, address(L2Bridge), 100, hex""); L2Bridge.finalizeBridgeETH{ value: 100 }(alice, address(L2Bridge), 100, hex"");
} }
/// @dev Tests that `finalizeDeposit` reverts if the receipient is the messenger.
function test_finalizeBridgeETH_sendToMessenger_reverts() external { function test_finalizeBridgeETH_sendToMessenger_reverts() external {
vm.mockCall( vm.mockCall(
address(L2Bridge.messenger()), address(L2Bridge.messenger()),
...@@ -543,6 +543,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer { ...@@ -543,6 +543,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
} }
contract L2StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer { contract L2StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer {
/// @dev Tests that `finalizeBridgeETH` succeeds.
function test_finalizeBridgeETH_succeeds() external { function test_finalizeBridgeETH_succeeds() external {
address messenger = address(L2Bridge.messenger()); address messenger = address(L2Bridge.messenger());
vm.mockCall( vm.mockCall(
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "./CommonTest.t.sol"; import { CommonTest } from "./CommonTest.t.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
// Libraries
import { Types } from "../libraries/Types.sol"; import { Types } from "../libraries/Types.sol";
import { Hashing } from "../libraries/Hashing.sol"; import { Hashing } from "../libraries/Hashing.sol";
// Target contract
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
contract L2ToL1MessagePasserTest is CommonTest { contract L2ToL1MessagePasserTest is CommonTest {
L2ToL1MessagePasser messagePasser; L2ToL1MessagePasser messagePasser;
...@@ -21,11 +26,14 @@ contract L2ToL1MessagePasserTest is CommonTest { ...@@ -21,11 +26,14 @@ contract L2ToL1MessagePasserTest is CommonTest {
event WithdrawerBalanceBurnt(uint256 indexed amount); event WithdrawerBalanceBurnt(uint256 indexed amount);
/// @dev Sets up the test suite.
function setUp() public virtual override { function setUp() public virtual override {
super.setUp(); super.setUp();
messagePasser = new L2ToL1MessagePasser(); messagePasser = new L2ToL1MessagePasser();
} }
/// @dev Tests that `initiateWithdrawal` succeeds and correctly sets the state
/// of the message passer for the withdrawal hash.
function testFuzz_initiateWithdrawal_succeeds( function testFuzz_initiateWithdrawal_succeeds(
address _sender, address _sender,
address _target, address _target,
...@@ -60,7 +68,8 @@ contract L2ToL1MessagePasserTest is CommonTest { ...@@ -60,7 +68,8 @@ contract L2ToL1MessagePasserTest is CommonTest {
assertEq(vm.load(address(messagePasser), slot), bytes32(uint256(1))); assertEq(vm.load(address(messagePasser), slot), bytes32(uint256(1)));
} }
// Test: initiateWithdrawal should emit the correct log when called by a contract /// @dev Tests that `initiateWithdrawal` succeeds and emits the correct MessagePassed
/// log when called by a contract.
function test_initiateWithdrawal_fromContract_succeeds() external { function test_initiateWithdrawal_fromContract_succeeds() external {
bytes32 withdrawalHash = Hashing.hashWithdrawal( bytes32 withdrawalHash = Hashing.hashWithdrawal(
Types.WithdrawalTransaction( Types.WithdrawalTransaction(
...@@ -88,7 +97,8 @@ contract L2ToL1MessagePasserTest is CommonTest { ...@@ -88,7 +97,8 @@ contract L2ToL1MessagePasserTest is CommonTest {
messagePasser.initiateWithdrawal{ value: 100 }(address(4), 64000, hex""); messagePasser.initiateWithdrawal{ value: 100 }(address(4), 64000, hex"");
} }
// Test: initiateWithdrawal should emit the correct log when called by an EOA /// @dev Tests that `initiateWithdrawal` succeeds and emits the correct MessagePassed
/// log when called by an EOA.
function test_initiateWithdrawal_fromEOA_succeeds() external { function test_initiateWithdrawal_fromEOA_succeeds() external {
uint256 gasLimit = 64000; uint256 gasLimit = 64000;
address target = address(4); address target = address(4);
...@@ -114,7 +124,7 @@ contract L2ToL1MessagePasserTest is CommonTest { ...@@ -114,7 +124,7 @@ contract L2ToL1MessagePasserTest is CommonTest {
assertEq(nonce + 1, messagePasser.messageNonce()); assertEq(nonce + 1, messagePasser.messageNonce());
} }
// Test: burn should destroy the ETH held in the contract /// @dev Tests that `burn` succeeds and destroys the ETH held in the contract.
function test_burn_succeeds() external { function test_burn_succeeds() external {
messagePasser.initiateWithdrawal{ value: NON_ZERO_VALUE }( messagePasser.initiateWithdrawal{ value: NON_ZERO_VALUE }(
NON_ZERO_ADDRESS, NON_ZERO_ADDRESS,
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
// Testing utilities
import { FeeVault_Initializer } from "./CommonTest.t.sol"; import { FeeVault_Initializer } from "./CommonTest.t.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
// Libraries
import { Predeploys } from "../libraries/Predeploys.sol";
// Target contract dependencies
import { FeeVault } from "../universal/FeeVault.sol"; import { FeeVault } from "../universal/FeeVault.sol";
// Target contract
import { SequencerFeeVault } from "../L2/SequencerFeeVault.sol"; import { SequencerFeeVault } from "../L2/SequencerFeeVault.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { Predeploys } from "../libraries/Predeploys.sol";
contract SequencerFeeVault_Test is FeeVault_Initializer { contract SequencerFeeVault_Test is FeeVault_Initializer {
/// @dev Sets up the test suite.
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
vm.etch( vm.etch(
...@@ -19,14 +26,17 @@ contract SequencerFeeVault_Test is FeeVault_Initializer { ...@@ -19,14 +26,17 @@ contract SequencerFeeVault_Test is FeeVault_Initializer {
vm.label(Predeploys.SEQUENCER_FEE_WALLET, "SequencerFeeVault"); vm.label(Predeploys.SEQUENCER_FEE_WALLET, "SequencerFeeVault");
} }
/// @dev Tests that the minimum withdrawal amount is correct.
function test_minWithdrawalAmount_succeeds() external { function test_minWithdrawalAmount_succeeds() external {
assertEq(vault.MIN_WITHDRAWAL_AMOUNT(), NON_ZERO_VALUE); assertEq(vault.MIN_WITHDRAWAL_AMOUNT(), NON_ZERO_VALUE);
} }
/// @dev Tests that the l1 fee wallet is correct.
function test_constructor_succeeds() external { function test_constructor_succeeds() external {
assertEq(vault.l1FeeWallet(), recipient); assertEq(vault.l1FeeWallet(), recipient);
} }
/// @dev Tests that the fee vault is able to receive ETH.
function test_receive_succeeds() external { function test_receive_succeeds() external {
uint256 balance = address(vault).balance; uint256 balance = address(vault).balance;
...@@ -37,6 +47,8 @@ contract SequencerFeeVault_Test is FeeVault_Initializer { ...@@ -37,6 +47,8 @@ contract SequencerFeeVault_Test is FeeVault_Initializer {
assertEq(address(vault).balance, balance + 100); assertEq(address(vault).balance, balance + 100);
} }
/// @dev Tests that `withdraw` reverts if the balance is less than the minimum
/// withdrawal amount.
function test_withdraw_notEnough_reverts() external { function test_withdraw_notEnough_reverts() external {
assert(address(vault).balance < vault.MIN_WITHDRAWAL_AMOUNT()); assert(address(vault).balance < vault.MIN_WITHDRAWAL_AMOUNT());
...@@ -46,6 +58,7 @@ contract SequencerFeeVault_Test is FeeVault_Initializer { ...@@ -46,6 +58,7 @@ contract SequencerFeeVault_Test is FeeVault_Initializer {
vault.withdraw(); vault.withdraw();
} }
/// @dev Tests that `withdraw` successfully initiates a withdrawal to L1.
function test_withdraw_toL1_succeeds() external { function test_withdraw_toL1_succeeds() external {
uint256 amount = vault.MIN_WITHDRAWAL_AMOUNT() + 1; uint256 amount = vault.MIN_WITHDRAWAL_AMOUNT() + 1;
vm.deal(address(vault), amount); vm.deal(address(vault), amount);
...@@ -85,6 +98,7 @@ contract SequencerFeeVault_Test is FeeVault_Initializer { ...@@ -85,6 +98,7 @@ contract SequencerFeeVault_Test is FeeVault_Initializer {
} }
contract SequencerFeeVault_L2Withdrawal_Test is FeeVault_Initializer { contract SequencerFeeVault_L2Withdrawal_Test is FeeVault_Initializer {
/// @dev Sets up the test suite.
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
vm.etch( vm.etch(
...@@ -95,6 +109,7 @@ contract SequencerFeeVault_L2Withdrawal_Test is FeeVault_Initializer { ...@@ -95,6 +109,7 @@ contract SequencerFeeVault_L2Withdrawal_Test is FeeVault_Initializer {
vm.label(Predeploys.SEQUENCER_FEE_WALLET, "SequencerFeeVault"); vm.label(Predeploys.SEQUENCER_FEE_WALLET, "SequencerFeeVault");
} }
/// @dev Tests that `withdraw` successfully initiates a withdrawal to L2.
function test_withdraw_toL2_succeeds() external { function test_withdraw_toL2_succeeds() external {
uint256 amount = vault.MIN_WITHDRAWAL_AMOUNT() + 1; uint256 amount = vault.MIN_WITHDRAWAL_AMOUNT() + 1;
vm.deal(address(vault), amount); vm.deal(address(vault), amount);
......
...@@ -278,7 +278,7 @@ const check = { ...@@ -278,7 +278,7 @@ const check = {
await assertSemver( await assertSemver(
L2CrossDomainMessenger, L2CrossDomainMessenger,
'L2CrossDomainMessenger', 'L2CrossDomainMessenger',
'1.4.0' '1.4.1'
) )
const xDomainMessageSenderSlot = await signer.provider.getStorageAt( const xDomainMessageSenderSlot = await signer.provider.getStorageAt(
...@@ -351,7 +351,7 @@ const check = { ...@@ -351,7 +351,7 @@ const check = {
signer signer
) )
await assertSemver(GasPriceOracle, 'GasPriceOracle') await assertSemver(GasPriceOracle, 'GasPriceOracle', '1.0.1')
const decimals = await GasPriceOracle.decimals() const decimals = await GasPriceOracle.decimals()
assert(decimals.eq(6)) assert(decimals.eq(6))
...@@ -369,7 +369,7 @@ const check = { ...@@ -369,7 +369,7 @@ const check = {
signer signer
) )
await assertSemver(L2StandardBridge, 'L2StandardBridge', '1.1.0') await assertSemver(L2StandardBridge, 'L2StandardBridge', '1.1.1')
const OTHER_BRIDGE = await L2StandardBridge.OTHER_BRIDGE() const OTHER_BRIDGE = await L2StandardBridge.OTHER_BRIDGE()
assert(OTHER_BRIDGE !== hre.ethers.constants.AddressZero) assert(OTHER_BRIDGE !== hre.ethers.constants.AddressZero)
...@@ -392,7 +392,7 @@ const check = { ...@@ -392,7 +392,7 @@ const check = {
signer signer
) )
await assertSemver(SequencerFeeVault, 'SequencerFeeVault', '1.2.0') await assertSemver(SequencerFeeVault, 'SequencerFeeVault', '1.2.1')
const RECIPIENT = await SequencerFeeVault.RECIPIENT() const RECIPIENT = await SequencerFeeVault.RECIPIENT()
assert(RECIPIENT !== hre.ethers.constants.AddressZero) assert(RECIPIENT !== hre.ethers.constants.AddressZero)
...@@ -460,7 +460,7 @@ const check = { ...@@ -460,7 +460,7 @@ const check = {
signer signer
) )
await assertSemver(L1Block, 'L1Block') await assertSemver(L1Block, 'L1Block', '1.0.1')
await checkProxy(hre, 'L1Block', signer.provider) await checkProxy(hre, 'L1Block', signer.provider)
await assertProxy(hre, 'L1Block', signer.provider) await assertProxy(hre, 'L1Block', signer.provider)
...@@ -526,7 +526,7 @@ const check = { ...@@ -526,7 +526,7 @@ const check = {
signer signer
) )
await assertSemver(L2ERC721Bridge, 'L2ERC721Bridge', '1.1.0') await assertSemver(L2ERC721Bridge, 'L2ERC721Bridge', '1.1.1')
const MESSENGER = await L2ERC721Bridge.MESSENGER() const MESSENGER = await L2ERC721Bridge.MESSENGER()
assert(MESSENGER !== hre.ethers.constants.AddressZero) assert(MESSENGER !== hre.ethers.constants.AddressZero)
...@@ -599,7 +599,7 @@ const check = { ...@@ -599,7 +599,7 @@ const check = {
signer signer
) )
await assertSemver(BaseFeeVault, 'BaseFeeVault', '1.2.0') await assertSemver(BaseFeeVault, 'BaseFeeVault', '1.2.1')
const MIN_WITHDRAWAL_AMOUNT = await BaseFeeVault.MIN_WITHDRAWAL_AMOUNT() const MIN_WITHDRAWAL_AMOUNT = await BaseFeeVault.MIN_WITHDRAWAL_AMOUNT()
console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`) console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`)
...@@ -626,7 +626,7 @@ const check = { ...@@ -626,7 +626,7 @@ const check = {
signer signer
) )
await assertSemver(L1FeeVault, 'L1FeeVault', '1.2.0') await assertSemver(L1FeeVault, 'L1FeeVault', '1.2.1')
const MIN_WITHDRAWAL_AMOUNT = await L1FeeVault.MIN_WITHDRAWAL_AMOUNT() const MIN_WITHDRAWAL_AMOUNT = await L1FeeVault.MIN_WITHDRAWAL_AMOUNT()
console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`) console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`)
...@@ -654,7 +654,7 @@ const check = { ...@@ -654,7 +654,7 @@ const check = {
signer signer
) )
await assertSemver(L2ToL1MessagePasser, 'L2ToL1MessagePasser') await assertSemver(L2ToL1MessagePasser, 'L2ToL1MessagePasser', '1.0.1')
const MESSAGE_VERSION = await L2ToL1MessagePasser.MESSAGE_VERSION() const MESSAGE_VERSION = await L2ToL1MessagePasser.MESSAGE_VERSION()
console.log(` - MESSAGE_VERSION: ${MESSAGE_VERSION}`) console.log(` - MESSAGE_VERSION: ${MESSAGE_VERSION}`)
......
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