Commit 83872ec2 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into k0622/sec-90-fd-rpc-tests

parents b42a8e90 0bb1b923
...@@ -8,6 +8,7 @@ import calendar ...@@ -8,6 +8,7 @@ import calendar
import datetime import datetime
import time import time
import shutil import shutil
import http.client
import devnet.log_setup import devnet.log_setup
from devnet.genesis import GENESIS_TMPL from devnet.genesis import GENESIS_TMPL
...@@ -51,6 +52,10 @@ def main(): ...@@ -51,6 +52,10 @@ def main():
os.makedirs(devnet_dir, exist_ok=True) os.makedirs(devnet_dir, exist_ok=True)
run_command(['docker-compose', 'build', '--progress', 'plain'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir
})
if args.deploy: if args.deploy:
log.info('Devnet with upcoming smart contract deployments') log.info('Devnet with upcoming smart contract deployments')
devnet_deploy(paths) devnet_deploy(paths)
...@@ -88,12 +93,14 @@ def devnet_prestate(paths): ...@@ -88,12 +93,14 @@ def devnet_prestate(paths):
'PWD': paths.ops_bedrock_dir 'PWD': paths.ops_bedrock_dir
}) })
wait_up(8545) wait_up(8545)
wait_for_rpc_server('127.0.0.1:8545')
log.info('Bringing up L2.') log.info('Bringing up L2.')
run_command(['docker-compose', 'up', '-d', 'l2'], cwd=paths.ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'l2'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir 'PWD': paths.ops_bedrock_dir
}) })
wait_up(9545) wait_up(9545)
wait_for_rpc_server('127.0.0.1:9545')
log.info('Bringing up the services.') log.info('Bringing up the services.')
run_command(['docker-compose', 'up', '-d', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={
...@@ -114,6 +121,7 @@ def devnet_deploy(paths): ...@@ -114,6 +121,7 @@ def devnet_deploy(paths):
'PWD': paths.ops_bedrock_dir 'PWD': paths.ops_bedrock_dir
}) })
wait_up(8545) wait_up(8545)
wait_for_rpc_server('127.0.0.1:8545')
log.info('Generating network config.') log.info('Generating network config.')
devnet_cfg_orig = pjoin(paths.contracts_bedrock_dir, 'deploy-config', 'devnetL1.json') devnet_cfg_orig = pjoin(paths.contracts_bedrock_dir, 'deploy-config', 'devnetL1.json')
...@@ -124,16 +132,24 @@ def devnet_deploy(paths): ...@@ -124,16 +132,24 @@ def devnet_deploy(paths):
deploy_config['l1StartingBlockTag'] = 'earliest' deploy_config['l1StartingBlockTag'] = 'earliest'
write_json(devnet_cfg_orig, deploy_config) write_json(devnet_cfg_orig, deploy_config)
fqn = 'scripts/Deploy.s.sol:Deploy'
private_key = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
if os.path.exists(paths.addresses_json_path): if os.path.exists(paths.addresses_json_path):
log.info('Contracts already deployed.') log.info('Contracts already deployed.')
addresses = read_json(paths.addresses_json_path) addresses = read_json(paths.addresses_json_path)
else: else:
log.info('Deploying contracts.') log.info('Deploying contracts.')
run_command(['yarn', 'hardhat', '--network', 'devnetL1', 'deploy', '--tags', 'l1'], env={ run_command([
'CHAIN_ID': '900', 'forge', 'script', fqn, '--private-key', private_key,
'L1_RPC': 'http://localhost:8545', '--rpc-url', 'http://127.0.0.1:8545', '--broadcast'
'PRIVATE_KEY_DEPLOYER': 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' ], env={}, cwd=paths.contracts_bedrock_dir)
}, cwd=paths.contracts_bedrock_dir)
run_command([
'forge', 'script', fqn, '--private-key', private_key,
'--sig', 'sync()', '--rpc-url', 'http://127.0.0.1:8545', '--broadcast'
], env={}, cwd=paths.contracts_bedrock_dir)
contracts = os.listdir(paths.deployment_dir) contracts = os.listdir(paths.deployment_dir)
addresses = {} addresses = {}
for c in contracts: for c in contracts:
...@@ -148,12 +164,14 @@ def devnet_deploy(paths): ...@@ -148,12 +164,14 @@ def devnet_deploy(paths):
'CanonicalTransactionChain': '0x0000000000000000000000000000000000000000', 'CanonicalTransactionChain': '0x0000000000000000000000000000000000000000',
'BondManager': '0x0000000000000000000000000000000000000000', 'BondManager': '0x0000000000000000000000000000000000000000',
}) })
sdk_addresses['L1CrossDomainMessenger'] = addresses['Proxy__OVM_L1CrossDomainMessenger']
sdk_addresses['L1StandardBridge'] = addresses['Proxy__OVM_L1StandardBridge'] sdk_addresses['L1CrossDomainMessenger'] = addresses['L1CrossDomainMessengerProxy']
sdk_addresses['L1StandardBridge'] = addresses['L1StandardBridgeProxy']
sdk_addresses['OptimismPortal'] = addresses['OptimismPortalProxy'] sdk_addresses['OptimismPortal'] = addresses['OptimismPortalProxy']
sdk_addresses['L2OutputOracle'] = addresses['L2OutputOracleProxy'] sdk_addresses['L2OutputOracle'] = addresses['L2OutputOracleProxy']
write_json(paths.addresses_json_path, addresses) write_json(paths.addresses_json_path, addresses)
write_json(paths.sdk_addresses_json_path, sdk_addresses) write_json(paths.sdk_addresses_json_path, sdk_addresses)
log.info(f'Wrote sdk addresses to {paths.sdk_addresses_json_path}')
if os.path.exists(paths.genesis_l2_path): if os.path.exists(paths.genesis_l2_path):
log.info('L2 genesis and rollup configs already generated.') log.info('L2 genesis and rollup configs already generated.')
...@@ -178,6 +196,7 @@ def devnet_deploy(paths): ...@@ -178,6 +196,7 @@ def devnet_deploy(paths):
'PWD': paths.ops_bedrock_dir 'PWD': paths.ops_bedrock_dir
}) })
wait_up(9545) wait_up(9545)
wait_for_rpc_server('127.0.0.1:9545')
log.info('Bringing up everything else.') log.info('Bringing up everything else.')
run_command(['docker-compose', 'up', '-d', 'op-node', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'op-node', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={
...@@ -189,6 +208,26 @@ def devnet_deploy(paths): ...@@ -189,6 +208,26 @@ def devnet_deploy(paths):
log.info('Devnet ready.') log.info('Devnet ready.')
def wait_for_rpc_server(url):
log.info(f'Waiting for RPC server at {url}')
conn = http.client.HTTPConnection(url)
headers = {'Content-type': 'application/json'}
body = '{"id":1, "jsonrpc":"2.0", "method": "eth_chainId", "params":[]}'
while True:
try:
conn.request('POST', '/', body, headers)
response = conn.getresponse()
conn.close()
if response.status < 300:
log.info(f'RPC server at {url} ready')
return
except Exception as e:
log.info(f'Waiting for RPC server at {url}')
time.sleep(1)
def run_command(args, check=True, shell=False, cwd=None, env=None): def run_command(args, check=True, shell=False, cwd=None, env=None):
env = env if env else {} env = env if env else {}
return subprocess.run( return subprocess.run(
......
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.
This diff is collapsed.
...@@ -19,7 +19,7 @@ type Deployment struct { ...@@ -19,7 +19,7 @@ type Deployment struct {
DeployedBytecode hexutil.Bytes `json:"deployedBytecode"` DeployedBytecode hexutil.Bytes `json:"deployedBytecode"`
Devdoc json.RawMessage `json:"devdoc"` Devdoc json.RawMessage `json:"devdoc"`
Metadata string `json:"metadata"` Metadata string `json:"metadata"`
Receipt Receipt `json:"receipt"` Receipt json.RawMessage `json:"receipt"`
SolcInputHash string `json:"solcInputHash"` SolcInputHash string `json:"solcInputHash"`
StorageLayout solc.StorageLayout `json:"storageLayout"` StorageLayout solc.StorageLayout `json:"storageLayout"`
TransactionHash common.Hash `json:"transactionHash"` TransactionHash common.Hash `json:"transactionHash"`
......
...@@ -239,16 +239,16 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -239,16 +239,16 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
} }
} }
if config.FundDevAccounts {
FundDevAccounts(memDB)
SetPrecompileBalances(memDB)
}
stateDB, err := backend.Blockchain().State() stateDB, err := backend.Blockchain().State()
if err != nil { if err != nil {
return nil, err return nil, err
} }
if config.FundDevAccounts {
FundDevAccounts(stateDB)
SetPrecompileBalances(stateDB)
}
for _, dep := range deployments { for _, dep := range deployments {
st, err := stateDB.StorageTrie(dep.Address) st, err := stateDB.StorageTrie(dep.Address)
if err != nil { if err != nil {
......
...@@ -456,6 +456,10 @@ func (m *Metrics) SetPeerScores(allScores []store.PeerScores) { ...@@ -456,6 +456,10 @@ func (m *Metrics) SetPeerScores(allScores []store.PeerScores) {
m.PeerScores.WithLabelValues("blocksTimeInMesh").Observe(scores.Gossip.Blocks.TimeInMesh) m.PeerScores.WithLabelValues("blocksTimeInMesh").Observe(scores.Gossip.Blocks.TimeInMesh)
m.PeerScores.WithLabelValues("blocksMessageDeliveries").Observe(scores.Gossip.Blocks.MeshMessageDeliveries) m.PeerScores.WithLabelValues("blocksMessageDeliveries").Observe(scores.Gossip.Blocks.MeshMessageDeliveries)
m.PeerScores.WithLabelValues("blocksInvalidMessageDeliveries").Observe(scores.Gossip.Blocks.InvalidMessageDeliveries) m.PeerScores.WithLabelValues("blocksInvalidMessageDeliveries").Observe(scores.Gossip.Blocks.InvalidMessageDeliveries)
m.PeerScores.WithLabelValues("reqRespValidResponses").Observe(scores.ReqResp.ValidResponses)
m.PeerScores.WithLabelValues("reqRespErrorResponses").Observe(scores.ReqResp.ErrorResponses)
m.PeerScores.WithLabelValues("reqRespRejectedPayloads").Observe(scores.ReqResp.RejectedPayloads)
} }
} }
......
...@@ -39,8 +39,8 @@ FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeed ...@@ -39,8 +39,8 @@ FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeed
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 503574) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 503574)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot:test_resolvesCorrectly_succeeds() (gas: 491581) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot:test_resolvesCorrectly_succeeds() (gas: 491581)
FaultDisputeGame_Test:test_defendRoot_invalidMove_reverts() (gas: 13250) FaultDisputeGame_Test:test_defendRoot_invalidMove_reverts() (gas: 13250)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17409) FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17448)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17834) FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17873)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10337) FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10337)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8216) FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8216)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17691) FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17691)
...@@ -142,7 +142,7 @@ L1StandardBridge_FinalizeERC20Withdrawal_Test:test_finalizeERC20Withdrawal_succe ...@@ -142,7 +142,7 @@ L1StandardBridge_FinalizeERC20Withdrawal_Test:test_finalizeERC20Withdrawal_succe
L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_notMessenger_reverts() (gas: 31206) L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_notMessenger_reverts() (gas: 31206)
L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_notOtherBridge_reverts() (gas: 31562) L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_notOtherBridge_reverts() (gas: 31562)
L1StandardBridge_FinalizeETHWithdrawal_Test:test_finalizeETHWithdrawal_succeeds() (gas: 61722) L1StandardBridge_FinalizeETHWithdrawal_Test:test_finalizeETHWithdrawal_succeeds() (gas: 61722)
L1StandardBridge_Getter_Test:test_getters_succeeds() (gas: 32875) L1StandardBridge_Getter_Test:test_getters_succeeds() (gas: 26157)
L1StandardBridge_Initialize_Test:test_initialize_succeeds() (gas: 22050) L1StandardBridge_Initialize_Test:test_initialize_succeeds() (gas: 22050)
L1StandardBridge_Receive_Test:test_receive_succeeds() (gas: 610719) L1StandardBridge_Receive_Test:test_receive_succeeds() (gas: 610719)
L2CrossDomainMessenger_Test:test_messageVersion_succeeds() (gas: 8477) L2CrossDomainMessenger_Test:test_messageVersion_succeeds() (gas: 8477)
......
...@@ -13,3 +13,6 @@ deploy-config/mainnet-forked.json ...@@ -13,3 +13,6 @@ deploy-config/mainnet-forked.json
test-case-generator/fuzz test-case-generator/fuzz
.resource-metering.csv .resource-metering.csv
scripts/differential-testing/differential-testing scripts/differential-testing/differential-testing
deployments/900
deployments/901
deployments/hardhat
...@@ -92,19 +92,26 @@ yarn echidna:aliasing ...@@ -92,19 +92,26 @@ yarn echidna:aliasing
### Deployment ### Deployment
The smart contracts are deployed using `foundry` with a `hardhat-deploy` compatibility layer. When the contracts are deployed,
they will write a temp file to disk that can then be formatted into a `hardhat-deploy` style artifact by calling another script.
#### Configuration #### Configuration
1. Create or modify a file `<network-name>.ts` inside of the [`deploy-config`](./deploy-config/) folder. Create or modify a file `<network-name>.json` inside of the [`deploy-config`](./deploy-config/) folder.
2. Fill out this file according to the `deployConfigSpec` located inside of the [`hardhat.config.ts](./hardhat.config.ts) By default, the network name will be selected automatically based on the chainid. Alternatively, the `DEPLOYMENT_CONTEXT` env var can be used to override the network name.
3. Optionally: Run `npx hardhat generate-deploy-config --network <network-name>` to generate the associated JSON The spec for the deploy config is defined by the `deployConfigSpec` located inside of the [`hardhat.config.ts`](./hardhat.config.ts).
file. This is required if using `op-chain-ops`.
#### Execution #### Execution
1. Copy `.env.example` into `.env` 1. Set the env vars `ETH_RPC_URL`, `PRIVATE_KEY` and `ETHERSCAN_API_KEY` if contract verification is desired
2. Fill out the `L1_RPC` and `PRIVATE_KEY_DEPLOYER` environment variables in `.env` 1. Deploy the contracts with `forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url $ETH_RPC_URL --broadcast --private-key $PRIVATE_KEY`
3. Run `npx hardhat deploy --network <network-name>` to deploy the L1 contracts Pass the `--verify` flag to verify the deployments automatically with Etherscan.
4. Run `npx hardhat etherscan-verify --network <network-name> --sleep` to verify contracts on Etherscan 1. Generate the hardhat deploy artifacts with `forge script -vvv scripts/Deploy.s.sol:Deploy --sig 'sync()' --rpc-url $ETH_RPC_URL --broadcast --private-key $PRIVATE_KEY`
#### Deploying a single contract
All of the functions for deploying a single contract are `public` meaning that the `--sig` argument to `forge script` can be used to
target the deployment of a single contract.
## Tools ## Tools
......
...@@ -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;
} }
......
...@@ -8,10 +8,10 @@ import { ClonesWithImmutableArgs } from "@cwia/ClonesWithImmutableArgs.sol"; ...@@ -8,10 +8,10 @@ import { ClonesWithImmutableArgs } from "@cwia/ClonesWithImmutableArgs.sol";
import { import {
OwnableUpgradeable OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Semver } from "../universal/Semver.sol";
import { IDisputeGame } from "./interfaces/IDisputeGame.sol"; import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol"; import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol";
import { IVersioned } from "./interfaces/IVersioned.sol";
/// @title DisputeGameFactory /// @title DisputeGameFactory
/// @notice A factory contract for creating `IDisputeGame` contracts. All created dispute games /// @notice A factory contract for creating `IDisputeGame` contracts. All created dispute games
...@@ -19,7 +19,7 @@ import { IVersioned } from "./interfaces/IVersioned.sol"; ...@@ -19,7 +19,7 @@ import { IVersioned } from "./interfaces/IVersioned.sol";
/// time of the dispute game is packed tightly into the storage slot with the address of /// time of the dispute game is packed tightly into the storage slot with the address of
/// the dispute game. This is to make offchain discoverability of playable dispute games /// the dispute game. This is to make offchain discoverability of playable dispute games
/// easier. /// easier.
contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersioned { contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
/// @dev Allows for the creation of clone proxies with immutable arguments. /// @dev Allows for the creation of clone proxies with immutable arguments.
using ClonesWithImmutableArgs for address; using ClonesWithImmutableArgs for address;
...@@ -37,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion ...@@ -37,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion
GameId[] internal _disputeGameList; GameId[] internal _disputeGameList;
/// @notice constructs a new DisputeGameFactory contract. /// @notice constructs a new DisputeGameFactory contract.
constructor() OwnableUpgradeable() { constructor() OwnableUpgradeable() Semver(0, 0, 2) {
initialize(address(0)); initialize(address(0));
} }
...@@ -48,12 +48,6 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion ...@@ -48,12 +48,6 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion
_transferOwnership(_owner); _transferOwnership(_owner);
} }
/// @inheritdoc IVersioned
/// @custom:semver 0.0.2
function version() external pure returns (string memory) {
return "0.0.2";
}
/// @inheritdoc IDisputeGameFactory /// @inheritdoc IDisputeGameFactory
function gameCount() external view returns (uint256 gameCount_) { function gameCount() external view returns (uint256 gameCount_) {
gameCount_ = _disputeGameList.length; gameCount_ = _disputeGameList.length;
......
...@@ -2,13 +2,13 @@ ...@@ -2,13 +2,13 @@
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import { IDisputeGame } from "./interfaces/IDisputeGame.sol"; import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IVersioned } from "./interfaces/IVersioned.sol";
import { IFaultDisputeGame } from "./interfaces/IFaultDisputeGame.sol"; import { IFaultDisputeGame } from "./interfaces/IFaultDisputeGame.sol";
import { IInitializable } from "./interfaces/IInitializable.sol"; import { IInitializable } from "./interfaces/IInitializable.sol";
import { IBondManager } from "./interfaces/IBondManager.sol"; import { IBondManager } from "./interfaces/IBondManager.sol";
import { IBigStepper } from "./interfaces/IBigStepper.sol"; import { IBigStepper } from "./interfaces/IBigStepper.sol";
import { Clone } from "../libraries/Clone.sol"; import { Clone } from "../libraries/Clone.sol";
import { Semver } from "../universal/Semver.sol";
import { LibHashing } from "./lib/LibHashing.sol"; import { LibHashing } from "./lib/LibHashing.sol";
import { LibPosition } from "./lib/LibPosition.sol"; import { LibPosition } from "./lib/LibPosition.sol";
import { LibClock } from "./lib/LibClock.sol"; import { LibClock } from "./lib/LibClock.sol";
...@@ -18,7 +18,7 @@ import "../libraries/DisputeErrors.sol"; ...@@ -18,7 +18,7 @@ import "../libraries/DisputeErrors.sol";
/// @title FaultDisputeGame /// @title FaultDisputeGame
/// @notice An implementation of the `IFaultDisputeGame` interface. /// @notice An implementation of the `IFaultDisputeGame` interface.
contract FaultDisputeGame is IFaultDisputeGame, Clone { contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// State Vars // // State Vars //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
...@@ -40,9 +40,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -40,9 +40,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
/// @notice The root claim's position is always at gindex 1. /// @notice The root claim's position is always at gindex 1.
Position internal constant ROOT_POSITION = Position.wrap(1); Position internal constant ROOT_POSITION = Position.wrap(1);
/// @notice The current Semver of the FaultDisputeGame implementation.
string internal constant VERSION = "0.0.2";
/// @notice The starting timestamp of the game /// @notice The starting timestamp of the game
Timestamp public gameStart; Timestamp public gameStart;
...@@ -63,7 +60,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -63,7 +60,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
Claim _absolutePrestate, Claim _absolutePrestate,
uint256 _maxGameDepth, uint256 _maxGameDepth,
IBigStepper _vm IBigStepper _vm
) { ) Semver(0, 0, 2) {
ABSOLUTE_PRESTATE = _absolutePrestate; ABSOLUTE_PRESTATE = _absolutePrestate;
MAX_GAME_DEPTH = _maxGameDepth; MAX_GAME_DEPTH = _maxGameDepth;
VM = _vm; VM = _vm;
...@@ -376,9 +373,4 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -376,9 +373,4 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
}) })
); );
} }
/// @inheritdoc IVersioned
function version() external pure override returns (string memory version_) {
version_ = VERSION;
}
} }
...@@ -3,13 +3,12 @@ pragma solidity ^0.8.15; ...@@ -3,13 +3,12 @@ pragma solidity ^0.8.15;
import "../../libraries/DisputeTypes.sol"; import "../../libraries/DisputeTypes.sol";
import { IVersioned } from "./IVersioned.sol";
import { IBondManager } from "./IBondManager.sol"; import { IBondManager } from "./IBondManager.sol";
import { IInitializable } from "./IInitializable.sol"; import { IInitializable } from "./IInitializable.sol";
/// @title IDisputeGame /// @title IDisputeGame
/// @notice The generic interface for a DisputeGame contract. /// @notice The generic interface for a DisputeGame contract.
interface IDisputeGame is IInitializable, IVersioned { interface IDisputeGame is IInitializable {
/// @notice Emitted when the game is resolved. /// @notice Emitted when the game is resolved.
/// @param status The status of the game after resolution. /// @param status The status of the game after resolution.
event Resolved(GameStatus indexed status); event Resolved(GameStatus indexed status);
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/// @title IVersioned
/// @notice An interface for semantically versioned contracts.
interface IVersioned {
/// @notice Returns the semantic version of the contract
/// @return _version The semantic version of the contract
function version() external pure returns (string memory _version);
}
...@@ -6,34 +6,27 @@ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; ...@@ -6,34 +6,27 @@ import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/access/Ownable.sol";
/** /// @custom:predeploy 0x4200000000000000000000000000000000000042
* @custom:predeploy 0x4200000000000000000000000000000000000042 /// @title GovernanceToken
* @title GovernanceToken /// @notice The Optimism token used in governance and supporting voting and delegation. Implements
* @notice The Optimism token used in governance and supporting voting and delegation. Implements /// EIP 2612 allowing signed approvals. Contract is "owned" by a `MintManager` instance with
* EIP 2612 allowing signed approvals. Contract is "owned" by a `MintManager` instance with /// permission to the `mint` function only, for the purposes of enforcing the token
* permission to the `mint` function only, for the purposes of enforcing the token inflation /// inflation schedule.
* schedule.
*/
contract GovernanceToken is ERC20Burnable, ERC20Votes, Ownable { contract GovernanceToken is ERC20Burnable, ERC20Votes, Ownable {
/// @notice Constructs the GovernanceToken contract.
constructor() ERC20("Optimism", "OP") ERC20Permit("Optimism") {} constructor() ERC20("Optimism", "OP") ERC20Permit("Optimism") {}
/** /// @notice Allows the owner to mint tokens.
* @notice Allows the owner to mint tokens. /// @param _account The account receiving minted tokens.
* /// @param _amount The amount of tokens to mint.
* @param _account The account receiving minted tokens.
* @param _amount The amount of tokens to mint.
*/
function mint(address _account, uint256 _amount) public onlyOwner { function mint(address _account, uint256 _amount) public onlyOwner {
_mint(_account, _amount); _mint(_account, _amount);
} }
/** /// @notice Callback called after a token transfer.
* @notice Callback called after a token transfer. /// @param from The account sending tokens.
* /// @param to The account receiving tokens.
* @param from The account sending tokens. /// @param amount The amount of tokens being transfered.
* @param to The account receiving tokens.
* @param amount The amount of tokens being transfered.
*/
function _afterTokenTransfer( function _afterTokenTransfer(
address from, address from,
address to, address to,
...@@ -42,22 +35,16 @@ contract GovernanceToken is ERC20Burnable, ERC20Votes, Ownable { ...@@ -42,22 +35,16 @@ contract GovernanceToken is ERC20Burnable, ERC20Votes, Ownable {
super._afterTokenTransfer(from, to, amount); super._afterTokenTransfer(from, to, amount);
} }
/** /// @notice Internal mint function.
* @notice Internal mint function. /// @param to The account receiving minted tokens.
* /// @param amount The amount of tokens to mint.
* @param to The account receiving minted tokens.
* @param amount The amount of tokens to mint.
*/
function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) { function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
super._mint(to, amount); super._mint(to, amount);
} }
/** /// @notice Internal burn function.
* @notice Internal burn function. /// @param account The account that tokens will be burned from.
* /// @param amount The amount of tokens that will be burned.
* @param account The account that tokens will be burned from.
* @param amount The amount of tokens that will be burned.
*/
function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) { function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
super._burn(account, amount); super._burn(account, amount);
} }
......
...@@ -4,57 +4,41 @@ pragma solidity 0.8.15; ...@@ -4,57 +4,41 @@ pragma solidity 0.8.15;
import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/access/Ownable.sol";
import "./GovernanceToken.sol"; import "./GovernanceToken.sol";
/** /// @title MintManager
* @title MintManager /// @notice Set as `owner` of the governance token and responsible for the token inflation
* @notice Set as `owner` of the OP token and responsible for the token inflation schedule. /// schedule. Contract acts as the token "mint manager" with permission to the `mint`
* Contract acts as the token "mint manager" with permission to the `mint` function only. /// function only. Currently permitted to mint once per year of up to 2% of the total
* Currently permitted to mint once per year of up to 2% of the total token supply. /// token supply. Upgradable to allow changes in the inflation schedule.
* Upgradable to allow changes in the inflation schedule.
*/
contract MintManager is Ownable { contract MintManager is Ownable {
/** /// @notice The GovernanceToken that the MintManager can mint tokens
* @notice The GovernanceToken that the MintManager can mint tokens
*/
GovernanceToken public immutable governanceToken; GovernanceToken public immutable governanceToken;
/** /// @notice The amount of tokens that can be minted per year.
* @notice The amount of tokens that can be minted per year. The value is a fixed /// The value is a fixed point number with 4 decimals.
* point number with 4 decimals.
*/
uint256 public constant MINT_CAP = 20; // 2% uint256 public constant MINT_CAP = 20; // 2%
/** /// @notice The number of decimals for the MINT_CAP.
* @notice The number of decimals for the MINT_CAP.
*/
uint256 public constant DENOMINATOR = 1000; uint256 public constant DENOMINATOR = 1000;
/** /// @notice The amount of time that must pass before the MINT_CAP number of tokens can
* @notice The amount of time that must pass before the MINT_CAP number of tokens can /// be minted again.
* be minted again.
*/
uint256 public constant MINT_PERIOD = 365 days; uint256 public constant MINT_PERIOD = 365 days;
/** /// @notice Tracks the time of last mint.
* @notice Tracks the time of last mint.
*/
uint256 public mintPermittedAfter; uint256 public mintPermittedAfter;
/** /// @notice Constructs the MintManager contract.
* @param _upgrader The owner of this contract /// @param _upgrader The owner of this contract.
* @param _governanceToken The governance token this contract can mint /// @param _governanceToken The governance token this contract can mint tokens of.
* tokens of
*/
constructor(address _upgrader, address _governanceToken) { constructor(address _upgrader, address _governanceToken) {
transferOwnership(_upgrader); transferOwnership(_upgrader);
governanceToken = GovernanceToken(_governanceToken); governanceToken = GovernanceToken(_governanceToken);
} }
/** /// @notice Only the token owner is allowed to mint a certain amount of the
* @notice Only the token owner is allowed to mint a certain amount of OP per year. /// governance token per year.
* /// @param _account The account receiving minted tokens.
* @param _account Address to mint new tokens to. /// @param _amount The amount of tokens to mint.
* @param _amount Amount of tokens to be minted.
*/
function mint(address _account, uint256 _amount) public onlyOwner { function mint(address _account, uint256 _amount) public onlyOwner {
if (mintPermittedAfter > 0) { if (mintPermittedAfter > 0) {
require( require(
...@@ -72,11 +56,8 @@ contract MintManager is Ownable { ...@@ -72,11 +56,8 @@ contract MintManager is Ownable {
governanceToken.mint(_account, _amount); governanceToken.mint(_account, _amount);
} }
/** /// @notice Upgrade the owner of the governance token to a new MintManager.
* @notice Upgrade the owner of the governance token to a new MintManager. /// @param _newMintManager The MintManager to upgrade to.
*
* @param _newMintManager The MintManager to upgrade to.
*/
function upgrade(address _newMintManager) public onlyOwner { function upgrade(address _newMintManager) public onlyOwner {
require( require(
_newMintManager != address(0), _newMintManager != address(0),
......
// 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 { GovernanceToken } from "../governance/GovernanceToken.sol"; import { GovernanceToken } from "../governance/GovernanceToken.sol";
contract GovernanceToken_Test is CommonTest { contract GovernanceToken_Test is CommonTest {
...@@ -9,12 +12,14 @@ contract GovernanceToken_Test is CommonTest { ...@@ -9,12 +12,14 @@ contract GovernanceToken_Test is CommonTest {
address constant rando = address(0x5678); address constant rando = address(0x5678);
GovernanceToken internal gov; GovernanceToken internal gov;
/// @dev Sets up the test suite.
function setUp() public virtual override { function setUp() public virtual override {
super.setUp(); super.setUp();
vm.prank(owner); vm.prank(owner);
gov = new GovernanceToken(); gov = new GovernanceToken();
} }
/// @dev Tests that the constructor sets the correct initial state.
function test_constructor_succeeds() external { function test_constructor_succeeds() external {
assertEq(gov.owner(), owner); assertEq(gov.owner(), owner);
assertEq(gov.name(), "Optimism"); assertEq(gov.name(), "Optimism");
...@@ -23,6 +28,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -23,6 +28,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 0); assertEq(gov.totalSupply(), 0);
} }
/// @dev Tests that the owner can successfully call `mint`.
function test_mint_fromOwner_succeeds() external { function test_mint_fromOwner_succeeds() external {
// Mint 100 tokens. // Mint 100 tokens.
vm.prank(owner); vm.prank(owner);
...@@ -33,6 +39,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -33,6 +39,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 100); assertEq(gov.totalSupply(), 100);
} }
/// @dev Tests that `mint` reverts when called by a non-owner.
function test_mint_fromNotOwner_reverts() external { function test_mint_fromNotOwner_reverts() external {
// Mint 100 tokens as rando. // Mint 100 tokens as rando.
vm.prank(rando); vm.prank(rando);
...@@ -44,6 +51,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -44,6 +51,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 0); assertEq(gov.totalSupply(), 0);
} }
/// @dev Tests that the owner can successfully call `burn`.
function test_burn_succeeds() external { function test_burn_succeeds() external {
// Mint 100 tokens to rando. // Mint 100 tokens to rando.
vm.prank(owner); vm.prank(owner);
...@@ -58,6 +66,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -58,6 +66,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 50); assertEq(gov.totalSupply(), 50);
} }
/// @dev Tests that the owner can successfully call `burnFrom`.
function test_burnFrom_succeeds() external { function test_burnFrom_succeeds() external {
// Mint 100 tokens to rando. // Mint 100 tokens to rando.
vm.prank(owner); vm.prank(owner);
...@@ -76,6 +85,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -76,6 +85,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 50); assertEq(gov.totalSupply(), 50);
} }
/// @dev Tests that `transfer` correctly transfers tokens.
function test_transfer_succeeds() external { function test_transfer_succeeds() external {
// Mint 100 tokens to rando. // Mint 100 tokens to rando.
vm.prank(owner); vm.prank(owner);
...@@ -91,6 +101,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -91,6 +101,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 100); assertEq(gov.totalSupply(), 100);
} }
/// @dev Tests that `approve` correctly sets allowances.
function test_approve_succeeds() external { function test_approve_succeeds() external {
// Mint 100 tokens to rando. // Mint 100 tokens to rando.
vm.prank(owner); vm.prank(owner);
...@@ -104,6 +115,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -104,6 +115,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.allowance(rando, owner), 50); assertEq(gov.allowance(rando, owner), 50);
} }
/// @dev Tests that `transferFrom` correctly transfers tokens.
function test_transferFrom_succeeds() external { function test_transferFrom_succeeds() external {
// Mint 100 tokens to rando. // Mint 100 tokens to rando.
vm.prank(owner); vm.prank(owner);
...@@ -123,6 +135,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -123,6 +135,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 100); assertEq(gov.totalSupply(), 100);
} }
/// @dev Tests that `increaseAllowance` correctly increases allowances.
function test_increaseAllowance_succeeds() external { function test_increaseAllowance_succeeds() external {
// Mint 100 tokens to rando. // Mint 100 tokens to rando.
vm.prank(owner); vm.prank(owner);
...@@ -140,6 +153,7 @@ contract GovernanceToken_Test is CommonTest { ...@@ -140,6 +153,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.allowance(rando, owner), 100); assertEq(gov.allowance(rando, owner), 100);
} }
/// @dev Tests that `decreaseAllowance` correctly decreases allowances.
function test_decreaseAllowance_succeeds() external { function test_decreaseAllowance_succeeds() external {
// Mint 100 tokens to rando. // Mint 100 tokens to rando.
vm.prank(owner); vm.prank(owner);
......
// 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({
......
...@@ -25,7 +25,6 @@ contract L1StandardBridge_Getter_Test is Bridge_Initializer { ...@@ -25,7 +25,6 @@ contract L1StandardBridge_Getter_Test is Bridge_Initializer {
assert(L1Bridge.OTHER_BRIDGE() == L2Bridge); assert(L1Bridge.OTHER_BRIDGE() == L2Bridge);
assert(L1Bridge.messenger() == L1Messenger); assert(L1Bridge.messenger() == L1Messenger);
assert(L1Bridge.MESSENGER() == L1Messenger); assert(L1Bridge.MESSENGER() == L1Messenger);
assertEq(L1Bridge.version(), "1.1.1");
} }
} }
......
// 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 { CommonTest } from "./CommonTest.t.sol"; import { CommonTest } from "./CommonTest.t.sol";
import { MintManager } from "../governance/MintManager.sol";
// Target contract dependencies
import { GovernanceToken } from "../governance/GovernanceToken.sol"; import { GovernanceToken } from "../governance/GovernanceToken.sol";
// Target contract
import { MintManager } from "../governance/MintManager.sol";
contract MintManager_Initializer is CommonTest { contract MintManager_Initializer is CommonTest {
address constant owner = address(0x1234); address constant owner = address(0x1234);
address constant rando = address(0x5678); address constant rando = address(0x5678);
GovernanceToken internal gov; GovernanceToken internal gov;
MintManager internal manager; MintManager internal manager;
/// @dev Sets up the test suite.
function setUp() public virtual override { function setUp() public virtual override {
super.setUp(); super.setUp();
...@@ -26,9 +32,7 @@ contract MintManager_Initializer is CommonTest { ...@@ -26,9 +32,7 @@ contract MintManager_Initializer is CommonTest {
} }
contract MintManager_constructor_Test is MintManager_Initializer { contract MintManager_constructor_Test is MintManager_Initializer {
/** /// @dev Tests that the constructor properly configures the contract.
* @notice Tests that the constructor properly configures the contract.
*/
function test_constructor_succeeds() external { function test_constructor_succeeds() external {
assertEq(manager.owner(), owner); assertEq(manager.owner(), owner);
assertEq(address(manager.governanceToken()), address(gov)); assertEq(address(manager.governanceToken()), address(gov));
...@@ -36,9 +40,7 @@ contract MintManager_constructor_Test is MintManager_Initializer { ...@@ -36,9 +40,7 @@ contract MintManager_constructor_Test is MintManager_Initializer {
} }
contract MintManager_mint_Test is MintManager_Initializer { contract MintManager_mint_Test is MintManager_Initializer {
/** /// @dev Tests that the mint function properly mints tokens when called by the owner.
* @notice Tests that the mint function properly mints tokens when called by the owner.
*/
function test_mint_fromOwner_succeeds() external { function test_mint_fromOwner_succeeds() external {
// Mint once. // Mint once.
vm.prank(owner); vm.prank(owner);
...@@ -48,9 +50,7 @@ contract MintManager_mint_Test is MintManager_Initializer { ...@@ -48,9 +50,7 @@ contract MintManager_mint_Test is MintManager_Initializer {
assertEq(gov.balanceOf(owner), 100); assertEq(gov.balanceOf(owner), 100);
} }
/** /// @dev Tests that the mint function reverts when called by a non-owner.
* @notice Tests that the mint function reverts when called by a non-owner.
*/
function test_mint_fromNotOwner_reverts() external { function test_mint_fromNotOwner_reverts() external {
// Mint from rando fails. // Mint from rando fails.
vm.prank(rando); vm.prank(rando);
...@@ -58,10 +58,8 @@ contract MintManager_mint_Test is MintManager_Initializer { ...@@ -58,10 +58,8 @@ contract MintManager_mint_Test is MintManager_Initializer {
manager.mint(owner, 100); manager.mint(owner, 100);
} }
/** /// @dev Tests that the mint function properly mints tokens when called by the owner a second
* @notice Tests that the mint function properly mints tokens when called by the owner a second /// time after the mint period has elapsed.
* time after the mint period has elapsed.
*/
function test_mint_afterPeriodElapsed_succeeds() external { function test_mint_afterPeriodElapsed_succeeds() external {
// Mint once. // Mint once.
vm.prank(owner); vm.prank(owner);
...@@ -79,10 +77,8 @@ contract MintManager_mint_Test is MintManager_Initializer { ...@@ -79,10 +77,8 @@ contract MintManager_mint_Test is MintManager_Initializer {
assertEq(gov.balanceOf(owner), 102); assertEq(gov.balanceOf(owner), 102);
} }
/** /// @dev Tests that the mint function always reverts when called before the mint period has
* @notice Tests that the mint function always reverts when called before the mint period has /// elapsed, even if the caller is the owner.
* elapsed, even if the caller is the owner.
*/
function test_mint_beforePeriodElapsed_reverts() external { function test_mint_beforePeriodElapsed_reverts() external {
// Mint once. // Mint once.
vm.prank(owner); vm.prank(owner);
...@@ -100,9 +96,7 @@ contract MintManager_mint_Test is MintManager_Initializer { ...@@ -100,9 +96,7 @@ contract MintManager_mint_Test is MintManager_Initializer {
assertEq(gov.balanceOf(owner), 100); assertEq(gov.balanceOf(owner), 100);
} }
/** /// @dev Tests that the owner cannot mint more than the mint cap.
* @notice Tests that the owner cannot mint more than the mint cap.
*/
function test_mint_moreThanCap_reverts() external { function test_mint_moreThanCap_reverts() external {
// Mint once. // Mint once.
vm.prank(owner); vm.prank(owner);
...@@ -123,9 +117,7 @@ contract MintManager_mint_Test is MintManager_Initializer { ...@@ -123,9 +117,7 @@ contract MintManager_mint_Test is MintManager_Initializer {
} }
contract MintManager_upgrade_Test is MintManager_Initializer { contract MintManager_upgrade_Test is MintManager_Initializer {
/** /// @dev Tests that the owner can upgrade the mint manager.
* @notice Tests that the owner can upgrade the mint manager.
*/
function test_upgrade_fromOwner_succeeds() external { function test_upgrade_fromOwner_succeeds() external {
// Upgrade to new manager. // Upgrade to new manager.
vm.prank(owner); vm.prank(owner);
...@@ -135,9 +127,7 @@ contract MintManager_upgrade_Test is MintManager_Initializer { ...@@ -135,9 +127,7 @@ contract MintManager_upgrade_Test is MintManager_Initializer {
assertEq(gov.owner(), rando); assertEq(gov.owner(), rando);
} }
/** /// @dev Tests that the upgrade function reverts when called by a non-owner.
* @notice Tests that the upgrade function reverts when called by a non-owner.
*/
function test_upgrade_fromNotOwner_reverts() external { function test_upgrade_fromNotOwner_reverts() external {
// Upgrade from rando fails. // Upgrade from rando fails.
vm.prank(rando); vm.prank(rando);
...@@ -145,10 +135,8 @@ contract MintManager_upgrade_Test is MintManager_Initializer { ...@@ -145,10 +135,8 @@ contract MintManager_upgrade_Test is MintManager_Initializer {
manager.upgrade(rando); manager.upgrade(rando);
} }
/** /// @dev Tests that the upgrade function reverts when attempting to update to the zero
* @notice Tests that the upgrade function reverts when attempting to update to the zero /// address, even if the caller is the owner.
* address, even if the caller is the owner.
*/
function test_upgrade_toZeroAddress_reverts() external { function test_upgrade_toZeroAddress_reverts() external {
// Upgrade to zero address fails. // Upgrade to zero address fails.
vm.prank(owner); vm.prank(owner);
......
// 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);
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"l2OutputOracleSubmissionInterval": 20, "l2OutputOracleSubmissionInterval": 20,
"l2OutputOracleStartingTimestamp": -1, "l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", "l2OutputOracleChallenger": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65",
"l2GenesisBlockGasLimit": "0x1c9c380", "l2GenesisBlockGasLimit": "0x1c9c380",
...@@ -40,7 +41,7 @@ ...@@ -40,7 +41,7 @@
"governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096",
"eip1559Denominator": 8, "eip1559Denominator": 8,
"eip1559Elasticity": 2, "eip1559Elasticity": 2,
"l1GenesisBlockTimestamp": "0x648a0943", "l1GenesisBlockTimestamp": "0x64935846",
"l1StartingBlockTag": "earliest", "l1StartingBlockTag": "earliest",
"l2GenesisRegolithTimeOffset": "0x0" "l2GenesisRegolithTimeOffset": "0x0"
} }
\ No newline at end of file
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
"l2OutputOracleSubmissionInterval": 6, "l2OutputOracleSubmissionInterval": 6,
"l2OutputOracleStartingTimestamp": 0, "l2OutputOracleStartingTimestamp": 0,
"l2OutputOracleStartingBlockNumber": 0, "l2OutputOracleStartingBlockNumber": 0,
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x6925B8704Ff96DEe942623d6FB5e946EF5884b63", "l2OutputOracleChallenger": "0x6925B8704Ff96DEe942623d6FB5e946EF5884b63",
"l2GenesisBlockBaseFeePerGas": "0x3B9ACA00", "l2GenesisBlockBaseFeePerGas": "0x3B9ACA00",
...@@ -34,5 +36,8 @@ ...@@ -34,5 +36,8 @@
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"finalizationPeriodSeconds": 2, "finalizationPeriodSeconds": 2,
"numDeployConfirmations": 1 "numDeployConfirmations": 1,
"eip1559Denominator": 50,
"eip1559Elasticity": 10,
"l2GenesisRegolithTimeOffset": "0x0"
} }
\ No newline at end of file
...@@ -22,7 +22,10 @@ no_match_contract = 'EchidnaFuzz' ...@@ -22,7 +22,10 @@ no_match_contract = 'EchidnaFuzz'
fs_permissions = [ fs_permissions = [
{ 'access'='read-write', 'path'='./.resource-metering.csv' }, { 'access'='read-write', 'path'='./.resource-metering.csv' },
{ 'access'='read', 'path'='./deployments/' }, { 'access'='read-write', 'path'='./deployments/' },
{ 'access'='read', 'path'='./deploy-config/' },
{ 'access'='read', 'path'='./broadcast/' },
{ access = 'read', path = './forge-artifacts/' },
] ]
[profile.ci] [profile.ci]
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
"build:fuzz": "(cd test-case-generator && go build ./cmd/fuzz.go)", "build:fuzz": "(cd test-case-generator && go build ./cmd/fuzz.go)",
"autogen:artifacts": "ts-node scripts/generate-artifacts.ts", "autogen:artifacts": "ts-node scripts/generate-artifacts.ts",
"autogen:invariant-docs": "ts-node scripts/invariant-doc-gen.ts", "autogen:invariant-docs": "ts-node scripts/invariant-doc-gen.ts",
"deploy": "hardhat deploy",
"test": "yarn build:differential && yarn build:fuzz && forge test", "test": "yarn build:differential && yarn build:fuzz && forge test",
"coverage": "yarn build:differential && yarn build:fuzz && forge coverage", "coverage": "yarn build:differential && yarn build:fuzz && forge coverage",
"coverage:lcov": "yarn build:differential && yarn build:fuzz && forge coverage --report lcov", "coverage:lcov": "yarn build:differential && yarn build:fuzz && forge coverage --report lcov",
......
This diff is collapsed.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Script } from "forge-std/Script.sol";
import { console2 as console } from "forge-std/console2.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { Executables } from "./Executables.sol";
/// @title DeployConfig
/// @notice Represents the configuration required to deploy the system. It is expected
/// to read the file from JSON. A future improvement would be to have fallback
/// values if they are not defined in the JSON themselves.
contract DeployConfig is Script {
string internal _json;
address public finalSystemOwner;
address public controller;
address public portalGuardian;
uint256 public l1ChainID;
uint256 public l2ChainID;
uint256 public l2BlockTime;
uint256 public maxSequencerDrift;
uint256 public sequencerWindowSize;
uint256 public channelTimeout;
address public p2pSequencerAddress;
address public batchInboxAddress;
address public batchSenderAddress;
uint256 public l2OutputOracleSubmissionInterval;
int256 internal _l2OutputOracleStartingTimestamp;
uint256 public l2OutputOracleStartingBlockNumber;
address public l2OutputOracleProposer;
address public l2OutputOracleChallenger;
uint256 public finalizationPeriodSeconds;
address public proxyAdminOwner;
address public baseFeeVaultRecipient;
address public l1FeeVaultRecipient;
address public sequencerFeeVaultRecipient;
string public governanceTokenName;
string public governanceTokenSymbol;
address public governanceTokenOwner;
uint256 public l2GenesisBlockGasLimit;
uint256 public l2GenesisBlockBaseFeePerGas;
uint256 public gasPriceOracleOverhead;
uint256 public gasPriceOracleScalar;
uint256 public eip1559Denominator;
uint256 public eip1559Elasticity;
uint256 public l2GenesisRegolithTimeOffset;
constructor(string memory _path) {
console.log("DeployConfig: reading file %s", _path);
_json = vm.readFile(_path);
finalSystemOwner = stdJson.readAddress(_json, "$.finalSystemOwner");
controller = stdJson.readAddress(_json, "$.controller");
portalGuardian = stdJson.readAddress(_json, "$.portalGuardian");
l1ChainID = stdJson.readUint(_json, "$.l1ChainID");
l2ChainID = stdJson.readUint(_json, "$.l2ChainID");
l2BlockTime = stdJson.readUint(_json, "$.l2BlockTime");
maxSequencerDrift = stdJson.readUint(_json, "$.maxSequencerDrift");
sequencerWindowSize = stdJson.readUint(_json, "$.sequencerWindowSize");
channelTimeout = stdJson.readUint(_json, "$.channelTimeout");
p2pSequencerAddress = stdJson.readAddress(_json, "$.p2pSequencerAddress");
batchInboxAddress = stdJson.readAddress(_json, "$.batchInboxAddress");
batchSenderAddress = stdJson.readAddress(_json, "$.batchSenderAddress");
l2OutputOracleSubmissionInterval = stdJson.readUint(_json, "$.l2OutputOracleSubmissionInterval");
_l2OutputOracleStartingTimestamp = stdJson.readInt(_json, "$.l2OutputOracleStartingTimestamp");
l2OutputOracleStartingBlockNumber = stdJson.readUint(_json, "$.l2OutputOracleStartingBlockNumber");
l2OutputOracleProposer = stdJson.readAddress(_json, "$.l2OutputOracleProposer");
l2OutputOracleChallenger = stdJson.readAddress(_json, "$.l2OutputOracleChallenger");
finalizationPeriodSeconds = stdJson.readUint(_json, "$.finalizationPeriodSeconds");
proxyAdminOwner = stdJson.readAddress(_json, "$.proxyAdminOwner");
baseFeeVaultRecipient = stdJson.readAddress(_json, "$.baseFeeVaultRecipient");
l1FeeVaultRecipient = stdJson.readAddress(_json, "$.l1FeeVaultRecipient");
sequencerFeeVaultRecipient = stdJson.readAddress(_json, "$.sequencerFeeVaultRecipient");
governanceTokenName = stdJson.readString(_json, "$.governanceTokenName");
governanceTokenSymbol = stdJson.readString(_json, "$.governanceTokenSymbol");
governanceTokenOwner = stdJson.readAddress(_json, "$.governanceTokenOwner");
l2GenesisBlockGasLimit = stdJson.readUint(_json, "$.l2GenesisBlockGasLimit");
l2GenesisBlockBaseFeePerGas = stdJson.readUint(_json, "$.l2GenesisBlockBaseFeePerGas");
gasPriceOracleOverhead = stdJson.readUint(_json, "$.gasPriceOracleOverhead");
gasPriceOracleScalar = stdJson.readUint(_json, "$.gasPriceOracleScalar");
eip1559Denominator = stdJson.readUint(_json, "$.eip1559Denominator");
eip1559Elasticity = stdJson.readUint(_json, "$.eip1559Elasticity");
l2GenesisRegolithTimeOffset = stdJson.readUint(_json, "$.l2GenesisRegolithTimeOffset");
}
function l1StartingBlockTag() public returns (bytes32) {
try vm.parseJsonBytes32(_json, "$.l1StartingBlockTag") returns (bytes32 tag) {
return tag;
} catch {
try vm.parseJsonString(_json, "$.l1StartingBlockTag") returns (string memory tag) {
return _getBlockByTag(tag);
} catch {
try vm.parseJsonUint(_json, "$.l1StartingBlockTag") returns (uint256 tag) {
return _getBlockByTag(vm.toString(tag));
} catch {}
}
}
revert("l1StartingBlockTag must be a bytes32, string or uint256 or cannot fetch l1StartingBlockTag");
}
function l2OutputOracleStartingTimestamp() public returns (uint256) {
if (_l2OutputOracleStartingTimestamp < 0) {
bytes32 tag = l1StartingBlockTag();
string[] memory cmd = new string[](3);
cmd[0] = Executables.bash;
cmd[1] = "-c";
cmd[2] = string.concat("cast block ", vm.toString(tag), " --json | ", Executables.jq, " .timestamp");
bytes memory res = vm.ffi(cmd);
return stdJson.readUint(string(res), "");
}
return uint256(_l2OutputOracleStartingTimestamp);
}
function _getBlockByTag(string memory _tag) internal returns (bytes32) {
string[] memory cmd = new string[](3);
cmd[0] = Executables.bash;
cmd[1] = "-c";
cmd[2] = string.concat("cast block ", _tag, " --json | ", Executables.jq, " -r .hash");
bytes memory res = vm.ffi(cmd);
return abi.decode(res, (bytes32));
}
}
This diff is collapsed.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice The executables used in ffi commands. These are set here
/// to have a single source of truth in case absolute paths
/// need to be used.
library Executables {
string internal constant bash = "bash";
string internal constant jq = "jq";
string internal constant forge = "forge";
}
...@@ -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