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
import datetime
import time
import shutil
import http.client
import devnet.log_setup
from devnet.genesis import GENESIS_TMPL
......@@ -51,6 +52,10 @@ def main():
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:
log.info('Devnet with upcoming smart contract deployments')
devnet_deploy(paths)
......@@ -88,12 +93,14 @@ def devnet_prestate(paths):
'PWD': paths.ops_bedrock_dir
})
wait_up(8545)
wait_for_rpc_server('127.0.0.1:8545')
log.info('Bringing up L2.')
run_command(['docker-compose', 'up', '-d', 'l2'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir
})
wait_up(9545)
wait_for_rpc_server('127.0.0.1:9545')
log.info('Bringing up the services.')
run_command(['docker-compose', 'up', '-d', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={
......@@ -114,6 +121,7 @@ def devnet_deploy(paths):
'PWD': paths.ops_bedrock_dir
})
wait_up(8545)
wait_for_rpc_server('127.0.0.1:8545')
log.info('Generating network config.')
devnet_cfg_orig = pjoin(paths.contracts_bedrock_dir, 'deploy-config', 'devnetL1.json')
......@@ -124,16 +132,24 @@ def devnet_deploy(paths):
deploy_config['l1StartingBlockTag'] = 'earliest'
write_json(devnet_cfg_orig, deploy_config)
fqn = 'scripts/Deploy.s.sol:Deploy'
private_key = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
if os.path.exists(paths.addresses_json_path):
log.info('Contracts already deployed.')
addresses = read_json(paths.addresses_json_path)
else:
log.info('Deploying contracts.')
run_command(['yarn', 'hardhat', '--network', 'devnetL1', 'deploy', '--tags', 'l1'], env={
'CHAIN_ID': '900',
'L1_RPC': 'http://localhost:8545',
'PRIVATE_KEY_DEPLOYER': 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
}, cwd=paths.contracts_bedrock_dir)
run_command([
'forge', 'script', fqn, '--private-key', private_key,
'--rpc-url', 'http://127.0.0.1:8545', '--broadcast'
], env={}, 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)
addresses = {}
for c in contracts:
......@@ -148,12 +164,14 @@ def devnet_deploy(paths):
'CanonicalTransactionChain': '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['L2OutputOracle'] = addresses['L2OutputOracleProxy']
write_json(paths.addresses_json_path, 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):
log.info('L2 genesis and rollup configs already generated.')
......@@ -178,6 +196,7 @@ def devnet_deploy(paths):
'PWD': paths.ops_bedrock_dir
})
wait_up(9545)
wait_for_rpc_server('127.0.0.1:9545')
log.info('Bringing up everything else.')
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):
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):
env = env if env else {}
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 {
DeployedBytecode hexutil.Bytes `json:"deployedBytecode"`
Devdoc json.RawMessage `json:"devdoc"`
Metadata string `json:"metadata"`
Receipt Receipt `json:"receipt"`
Receipt json.RawMessage `json:"receipt"`
SolcInputHash string `json:"solcInputHash"`
StorageLayout solc.StorageLayout `json:"storageLayout"`
TransactionHash common.Hash `json:"transactionHash"`
......
......@@ -239,16 +239,16 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
}
}
if config.FundDevAccounts {
FundDevAccounts(memDB)
SetPrecompileBalances(memDB)
}
stateDB, err := backend.Blockchain().State()
if err != nil {
return nil, err
}
if config.FundDevAccounts {
FundDevAccounts(stateDB)
SetPrecompileBalances(stateDB)
}
for _, dep := range deployments {
st, err := stateDB.StorageTrie(dep.Address)
if err != nil {
......
......@@ -456,6 +456,10 @@ func (m *Metrics) SetPeerScores(allScores []store.PeerScores) {
m.PeerScores.WithLabelValues("blocksTimeInMesh").Observe(scores.Gossip.Blocks.TimeInMesh)
m.PeerScores.WithLabelValues("blocksMessageDeliveries").Observe(scores.Gossip.Blocks.MeshMessageDeliveries)
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
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 503574)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot:test_resolvesCorrectly_succeeds() (gas: 491581)
FaultDisputeGame_Test:test_defendRoot_invalidMove_reverts() (gas: 13250)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17409)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17834)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17448)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17873)
FaultDisputeGame_Test:test_gameStart_succeeds() (gas: 10337)
FaultDisputeGame_Test:test_gameType_succeeds() (gas: 8216)
FaultDisputeGame_Test:test_initialRootClaimData_succeeds() (gas: 17691)
......@@ -142,7 +142,7 @@ L1StandardBridge_FinalizeERC20Withdrawal_Test:test_finalizeERC20Withdrawal_succe
L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_notMessenger_reverts() (gas: 31206)
L1StandardBridge_FinalizeERC20Withdrawal_TestFail:test_finalizeERC20Withdrawal_notOtherBridge_reverts() (gas: 31562)
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_Receive_Test:test_receive_succeeds() (gas: 610719)
L2CrossDomainMessenger_Test:test_messageVersion_succeeds() (gas: 8477)
......
......@@ -13,3 +13,6 @@ deploy-config/mainnet-forked.json
test-case-generator/fuzz
.resource-metering.csv
scripts/differential-testing/differential-testing
deployments/900
deployments/901
deployments/hardhat
......@@ -92,19 +92,26 @@ yarn echidna:aliasing
### 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
1. Create or modify a file `<network-name>.ts` 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)
3. Optionally: Run `npx hardhat generate-deploy-config --network <network-name>` to generate the associated JSON
file. This is required if using `op-chain-ops`.
Create or modify a file `<network-name>.json` inside of the [`deploy-config`](./deploy-config/) folder.
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.
The spec for the deploy config is defined by the `deployConfigSpec` located inside of the [`hardhat.config.ts`](./hardhat.config.ts).
#### Execution
1. Copy `.env.example` into `.env`
2. Fill out the `L1_RPC` and `PRIVATE_KEY_DEPLOYER` environment variables in `.env`
3. Run `npx hardhat deploy --network <network-name>` to deploy the L1 contracts
4. Run `npx hardhat etherscan-verify --network <network-name> --sleep` to verify contracts on Etherscan
1. Set the env vars `ETH_RPC_URL`, `PRIVATE_KEY` and `ETHERSCAN_API_KEY` if contract verification is desired
1. Deploy the contracts with `forge script -vvv scripts/Deploy.s.sol:Deploy --rpc-url $ETH_RPC_URL --broadcast --private-key $PRIVATE_KEY`
Pass the `--verify` flag to verify the deployments automatically with 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
......
......@@ -4,23 +4,19 @@ pragma solidity 0.8.15;
import { Semver } from "../universal/Semver.sol";
import { FeeVault } from "../universal/FeeVault.sol";
/**
* @custom:proxied
* @custom:predeploy 0x4200000000000000000000000000000000000019
* @title BaseFeeVault
* @notice The BaseFeeVault accumulates the base fee that is paid by transactions.
*/
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000019
/// @title BaseFeeVault
/// @notice The BaseFeeVault accumulates the base fee that is paid by transactions.
contract BaseFeeVault is FeeVault, Semver {
/**
* @custom:semver 1.2.0
*
* @param _recipient Wallet that will receive the fees.
* @param _minWithdrawalAmount Minimum balance for withdrawals.
* @param _withdrawalNetwork Network which the recipient will receive fees on.
*/
/// @custom:semver 1.2.1
/// @notice Constructs the BaseFeeVault contract.
/// @param _recipient Wallet that will receive the fees.
/// @param _minWithdrawalAmount Minimum balance for withdrawals.
/// @param _withdrawalNetwork Network which the recipient will receive fees on.
constructor(
address _recipient,
uint256 _minWithdrawalAmount,
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;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
/**
* @title CrossDomainOwnable
* @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
* CrossDomainMessenger system is bypassed and the caller on L1 is calling the
* OptimismPortal directly.
*/
/// @title CrossDomainOwnable
/// @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
/// CrossDomainMessenger system is bypassed and the caller on L1 is calling the
/// OptimismPortal directly.
abstract contract CrossDomainOwnable is Ownable {
/**
* @notice Overrides the implementation of the `onlyOwner` modifier to check that the unaliased
* `msg.sender` is the owner of the contract.
*/
/// @notice Overrides the implementation of the `onlyOwner` modifier to check that the unaliased
/// `msg.sender` is the owner of the contract.
function _checkOwner() internal view override {
require(
owner() == AddressAliasHelper.undoL1ToL2Alias(msg.sender),
......
......@@ -5,19 +5,15 @@ import { Predeploys } from "../libraries/Predeploys.sol";
import { L2CrossDomainMessenger } from "./L2CrossDomainMessenger.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title CrossDomainOwnable2
* @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
* the CrossDomainMessenger system. It will not work if the OptimismPortal is used
* directly.
*/
/// @title CrossDomainOwnable2
/// @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
/// the CrossDomainMessenger system. It will not work if the OptimismPortal is used
/// directly.
abstract contract CrossDomainOwnable2 is Ownable {
/**
* @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
* of the L1CrossDomainMessenger.
*/
/// @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
/// of the L1CrossDomainMessenger.
function _checkOwner() internal view override {
L2CrossDomainMessenger messenger = L2CrossDomainMessenger(
Predeploys.L2_CROSS_DOMAIN_MESSENGER
......
......@@ -5,35 +5,30 @@ import { Predeploys } from "../libraries/Predeploys.sol";
import { L2CrossDomainMessenger } from "./L2CrossDomainMessenger.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
/**
* @title CrossDomainOwnable3
* @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 systems
* that use the CrossDomainMessenger system. It will not work if the OptimismPortal is
* used directly.
*/
/// @title CrossDomainOwnable3
/// @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
/// systems that use the CrossDomainMessenger system. It will not work if the
/// OptimismPortal is used directly.
abstract contract CrossDomainOwnable3 is Ownable {
/**
* @notice If true, the contract uses the cross domain _checkOwner function override. If false
* it uses the standard Ownable _checkOwner function.
*/
/// @notice If true, the contract uses the cross domain _checkOwner function override.
/// If false it uses the standard Ownable _checkOwner function.
bool public isLocal = true;
/**
* @notice Emits when ownership of the contract is transferred. Includes the
* isLocal field in addition to the standard `Ownable` OwnershipTransferred event.
*/
/// @notice Emits when ownership of the contract is transferred. Includes the
/// 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(
address indexed previousOwner,
address indexed newOwner,
bool isLocal
);
/**
* @notice Allows for ownership to be transferred with specifying the locality.
* @param _owner The new owner of the contract.
* @param _isLocal Configures the locality of the ownership.
*/
/// @notice Allows for ownership to be transferred with specifying the locality.
/// @param _owner The new owner of the contract.
/// @param _isLocal Configures the locality of the ownership.
function transferOwnership(address _owner, bool _isLocal) external onlyOwner {
require(_owner != address(0), "CrossDomainOwnable3: new owner is the zero address");
......@@ -44,11 +39,9 @@ abstract contract CrossDomainOwnable3 is Ownable {
emit OwnershipTransferred(oldOwner, _owner, _isLocal);
}
/**
* @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
* of the L1CrossDomainMessenger.
*/
/// @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
/// of the L1CrossDomainMessenger.
function _checkOwner() internal view override {
if (isLocal) {
require(owner() == msg.sender, "CrossDomainOwnable3: caller is not the owner");
......
......@@ -5,41 +5,32 @@ import { Semver } from "../universal/Semver.sol";
import { Predeploys } from "../libraries/Predeploys.sol";
import { L1Block } from "../L2/L1Block.sol";
/**
* @custom:proxied
* @custom:predeploy 0x420000000000000000000000000000000000000F
* @title GasPriceOracle
* @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
* 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
* 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
* transaction fee will be. The following events were deprecated with Bedrock:
* - event OverheadUpdated(uint256 overhead);
* - event ScalarUpdated(uint256 scalar);
* - event DecimalsUpdated(uint256 decimals);
*/
/// @custom:proxied
/// @custom:predeploy 0x420000000000000000000000000000000000000F
/// @title GasPriceOracle
/// @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
/// 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
/// 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
/// transaction fee will be. The following events were deprecated with Bedrock:
/// - event OverheadUpdated(uint256 overhead);
/// - event ScalarUpdated(uint256 scalar);
/// - event DecimalsUpdated(uint256 decimals);
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;
/**
* @custom:semver 1.0.0
*/
constructor() Semver(1, 0, 0) {}
/// @custom:semver 1.0.1
/// @notice Constructs the GasPriceOracle contract.
constructor() Semver(1, 0, 1) {}
/**
* @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.
*
* @param _data Unsigned fully RLP-encoded transaction to get the L1 fee for.
*
* @return L1 fee that should be paid for the tx
*/
/// @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.
/// @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) {
uint256 l1GasUsed = getL1GasUsed(_data);
uint256 l1Fee = l1GasUsed * l1BaseFee();
......@@ -49,71 +40,49 @@ contract GasPriceOracle is Semver {
return scaled;
}
/**
* @notice Retrieves the current gas price (base fee).
*
* @return Current L2 gas price (base fee).
*/
/// @notice Retrieves the current gas price (base fee).
/// @return Current L2 gas price (base fee).
function gasPrice() public view returns (uint256) {
return block.basefee;
}
/**
* @notice Retrieves the current base fee.
*
* @return Current L2 base fee.
*/
/// @notice Retrieves the current base fee.
/// @return Current L2 base fee.
function baseFee() public view returns (uint256) {
return block.basefee;
}
/**
* @notice Retrieves the current fee overhead.
*
* @return Current fee overhead.
*/
/// @notice Retrieves the current fee overhead.
/// @return Current fee overhead.
function overhead() public view returns (uint256) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeOverhead();
}
/**
* @notice Retrieves the current fee scalar.
*
* @return Current fee scalar.
*/
/// @notice Retrieves the current fee scalar.
/// @return Current fee scalar.
function scalar() public view returns (uint256) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).l1FeeScalar();
}
/**
* @notice Retrieves the latest known L1 base fee.
*
* @return Latest known L1 base fee.
*/
/// @notice Retrieves the latest known L1 base fee.
/// @return Latest known L1 base fee.
function l1BaseFee() public view returns (uint256) {
return L1Block(Predeploys.L1_BLOCK_ATTRIBUTES).basefee();
}
/**
* @custom:legacy
* @notice Retrieves the number of decimals used in the scalar.
*
* @return Number of decimals used in the scalar.
*/
/// @custom:legacy
/// @notice Retrieves the number of decimals used in the scalar.
/// @return Number of decimals used in the scalar.
function decimals() public pure returns (uint256) {
return DECIMALS;
}
/**
* @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
* roots to L1. Adds 68 bytes of padding to account for the fact that the input does
* 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.
*/
/// @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
/// roots to L1. Adds 68 bytes of padding to account for the fact that the input does
/// 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.
function getL1GasUsed(bytes memory _data) public view returns (uint256) {
uint256 total = 0;
uint256 length = _data.length;
......
......@@ -3,79 +3,54 @@ pragma solidity 0.8.15;
import { Semver } from "../universal/Semver.sol";
/**
* @custom:proxied
* @custom:predeploy 0x4200000000000000000000000000000000000015
* @title L1Block
* @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
* set by the "depositor" account, a special system address. Depositor account transactions
* are created by the protocol whenever we move to a new epoch.
*/
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000015
/// @title L1Block
/// @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
/// set by the "depositor" account, a special system address. Depositor account transactions
/// are created by the protocol whenever we move to a new epoch.
contract L1Block is Semver {
/**
* @notice Address of the special depositor account.
*/
/// @notice Address of the special depositor account.
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;
/**
* @notice The latest L1 timestamp known by the L2 system.
*/
/// @notice The latest L1 timestamp known by the L2 system.
uint64 public timestamp;
/**
* @notice The latest L1 basefee.
*/
/// @notice The latest L1 basefee.
uint256 public basefee;
/**
* @notice The latest L1 blockhash.
*/
/// @notice The latest L1 blockhash.
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;
/**
* @notice The versioned hash to authenticate the batcher by.
*/
/// @notice The versioned hash to authenticate the batcher by.
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;
/**
* @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;
/**
* @custom:semver 1.0.0
*/
constructor() Semver(1, 0, 0) {}
/// @custom:semver 1.0.1
/// @notice Constructs the L1Block contract.
constructor() Semver(1, 0, 1) {}
/**
* @notice Updates the L1 block values.
*
* @param _number L1 blocknumber.
* @param _timestamp L1 timestamp.
* @param _basefee L1 basefee.
* @param _hash L1 blockhash.
* @param _sequenceNumber Number of L2 blocks since epoch start.
* @param _batcherHash Versioned hash to authenticate batcher by.
* @param _l1FeeOverhead L1 fee overhead.
* @param _l1FeeScalar L1 fee scalar.
*/
/// @notice Updates the L1 block values.
/// @param _number L1 blocknumber.
/// @param _timestamp L1 timestamp.
/// @param _basefee L1 basefee.
/// @param _hash L1 blockhash.
/// @param _sequenceNumber Number of L2 blocks since epoch start.
/// @param _batcherHash Versioned hash to authenticate batcher by.
/// @param _l1FeeOverhead L1 fee overhead.
/// @param _l1FeeScalar L1 fee scalar.
function setL1BlockValues(
uint64 _number,
uint64 _timestamp,
......
......@@ -4,23 +4,19 @@ pragma solidity 0.8.15;
import { Semver } from "../universal/Semver.sol";
import { FeeVault } from "../universal/FeeVault.sol";
/**
* @custom:proxied
* @custom:predeploy 0x420000000000000000000000000000000000001A
* @title L1FeeVault
* @notice The L1FeeVault accumulates the L1 portion of the transaction fees.
*/
/// @custom:proxied
/// @custom:predeploy 0x420000000000000000000000000000000000001A
/// @title L1FeeVault
/// @notice The L1FeeVault accumulates the L1 portion of the transaction fees.
contract L1FeeVault is FeeVault, Semver {
/**
* @custom:semver 1.2.0
*
* @param _recipient Wallet that will receive the fees.
* @param _minWithdrawalAmount Minimum balance for withdrawals.
* @param _withdrawalNetwork Network which the recipient will receive fees on.
*/
/// @custom:semver 1.2.1
/// @notice Constructs the L1FeeVault contract.
/// @param _recipient Wallet that will receive the fees.
/// @param _minWithdrawalAmount Minimum balance for withdrawals.
/// @param _withdrawalNetwork Network which the recipient will receive fees on.
constructor(
address _recipient,
uint256 _minWithdrawalAmount,
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";
import { Semver } from "../universal/Semver.sol";
import { L2ToL1MessagePasser } from "./L2ToL1MessagePasser.sol";
/**
* @custom:proxied
* @custom:predeploy 0x4200000000000000000000000000000000000007
* @title L2CrossDomainMessenger
* @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
* level message passing contracts.
*/
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000007
/// @title L2CrossDomainMessenger
/// @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
/// level message passing contracts.
contract L2CrossDomainMessenger is CrossDomainMessenger, Semver {
/**
* @custom:semver 1.4.0
*
* @param _l1CrossDomainMessenger Address of the L1CrossDomainMessenger contract.
*/
/// @custom:semver 1.4.1
/// @notice Constructs the L2CrossDomainMessenger contract.
/// @param _l1CrossDomainMessenger Address of the L1CrossDomainMessenger contract.
constructor(address _l1CrossDomainMessenger)
Semver(1, 4, 0)
Semver(1, 4, 1)
CrossDomainMessenger(_l1CrossDomainMessenger)
{
initialize();
}
/**
* @notice Initializer.
*/
/// @notice Initializer.
function initialize() public initializer {
__CrossDomainMessenger_init();
}
/**
* @custom:legacy
* @notice Legacy getter for the remote messenger. Use otherMessenger going forward.
*
* @return Address of the L1CrossDomainMessenger contract.
*/
/// @custom:legacy
/// @notice Legacy getter for the remote messenger.
/// Use otherMessenger going forward.
/// @return Address of the L1CrossDomainMessenger contract.
function l1CrossDomainMessenger() public view returns (address) {
return OTHER_MESSENGER;
}
/**
* @inheritdoc CrossDomainMessenger
*/
/// @inheritdoc CrossDomainMessenger
function _sendMessage(
address _to,
uint64 _gasLimit,
......@@ -59,16 +49,12 @@ contract L2CrossDomainMessenger is CrossDomainMessenger, Semver {
}(_to, _gasLimit, _data);
}
/**
* @inheritdoc CrossDomainMessenger
*/
/// @inheritdoc CrossDomainMessenger
function _isOtherMessenger() internal view override returns (bool) {
return AddressAliasHelper.undoL1ToL2Alias(msg.sender) == OTHER_MESSENGER;
}
/**
* @inheritdoc CrossDomainMessenger
*/
/// @inheritdoc CrossDomainMessenger
function _isUnsafeTarget(address _target) internal view override returns (bool) {
return _target == address(this) || _target == address(Predeploys.L2_TO_L1_MESSAGE_PASSER);
}
......
......@@ -7,42 +7,35 @@ import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { IOptimismMintableERC721 } from "../universal/IOptimismMintableERC721.sol";
import { Semver } from "../universal/Semver.sol";
/**
* @title L2ERC721Bridge
* @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
* 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.
* **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
* wait for the one-week challenge period to elapse before their Optimism-native NFT
* can be refunded on L2.
*/
/// @title L2ERC721Bridge
/// @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
/// 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.
/// **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
/// wait for the one-week challenge period to elapse before their Optimism-native NFT
/// can be refunded on L2.
contract L2ERC721Bridge is ERC721Bridge, Semver {
/**
* @custom:semver 1.1.0
*
* @param _messenger Address of the CrossDomainMessenger on this network.
* @param _otherBridge Address of the ERC721 bridge on the other network.
*/
/// @custom:semver 1.1.1
/// @notice Constructs the L2ERC721Bridge contract.
/// @param _messenger Address of the CrossDomainMessenger on this network.
/// @param _otherBridge Address of the ERC721 bridge on the other network.
constructor(address _messenger, address _otherBridge)
Semver(1, 1, 0)
Semver(1, 1, 1)
ERC721Bridge(_messenger, _otherBridge)
{}
/**
* @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
* 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 _from Address that triggered the bridge on the other domain.
* @param _to Address to receive the token on this domain.
* @param _tokenId ID of the token being deposited.
* @param _extraData Optional data to forward to L1. Data supplied here will not be used to
* execute any code on L1 and is only emitted as extra data for the
* convenience of off-chain tooling.
*/
/// @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
/// 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 _from Address that triggered the bridge on the other domain.
/// @param _to Address to receive the token on this domain.
/// @param _tokenId ID of the token being deposited.
/// @param _extraData Optional data to forward to L1.
/// Data supplied here will not be used to execute any code on L1 and is
/// only emitted as extra data for the convenience of off-chain tooling.
function finalizeBridgeERC721(
address _localToken,
address _remoteToken,
......@@ -73,9 +66,7 @@ contract L2ERC721Bridge is ERC721Bridge, Semver {
emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
/**
* @inheritdoc ERC721Bridge
*/
/// @inheritdoc ERC721Bridge
function _initiateBridgeERC721(
address _localToken,
address _remoteToken,
......
......@@ -7,46 +7,33 @@ import { Encoding } from "../libraries/Encoding.sol";
import { Burn } from "../libraries/Burn.sol";
import { Semver } from "../universal/Semver.sol";
/**
* @custom:proxied
* @custom:predeploy 0x4200000000000000000000000000000000000016
* @title L2ToL1MessagePasser
* @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
* of the L2 output to reduce the cost of proving the existence of sent messages.
*/
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000016
/// @title L2ToL1MessagePasser
/// @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
/// of the L2 output to reduce the cost of proving the existence of sent messages.
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;
/**
* @notice Current message version identifier.
*/
/// @notice The current message version identifier.
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;
/**
* @notice A unique value hashed with each withdrawal.
*/
/// @notice A unique value hashed with each withdrawal.
uint240 internal msgNonce;
/**
* @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 target The L1 account address the call will be send to.
* @param value The ETH value submitted for withdrawal, to be forwarded to the target.
* @param gasLimit The minimum amount of gas that must be provided when withdrawing.
* @param data The data to be forwarded to the target on L1.
* @param withdrawalHash The hash of the withdrawal.
*/
/// @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 target The L1 account address the call will be send to.
/// @param value The ETH value submitted for withdrawal, to be forwarded to the target.
/// @param gasLimit The minimum amount of gas that must be provided when withdrawing.
/// @param data The data to be forwarded to the target on L1.
/// @param withdrawalHash The hash of the withdrawal.
event MessagePassed(
uint256 indexed nonce,
address indexed sender,
......@@ -57,44 +44,33 @@ contract L2ToL1MessagePasser is Semver {
bytes32 withdrawalHash
);
/**
* @notice Emitted when the balance of this contract is burned.
*
* @param amount Amount of ETh that was burned.
*/
/// @notice Emitted when the balance of this contract is burned.
/// @param amount Amount of ETh that was burned.
event WithdrawerBalanceBurnt(uint256 indexed amount);
/**
* @custom:semver 1.0.0
*/
constructor() Semver(1, 0, 0) {}
/// @custom:semver 1.0.1
/// @notice Constructs the L2ToL1MessagePasser contract.
constructor() Semver(1, 0, 1) {}
/**
* @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 {
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
* 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
* incentivized since this function is very cheap.
*/
/// @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
/// create a contract and self-destruct it to itself. Anyone can call this function. Not
/// incentivized since this function is very cheap.
function burn() external {
uint256 balance = address(this).balance;
Burn.eth(balance);
emit WithdrawerBalanceBurnt(balance);
}
/**
* @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 _data Data to forward to L1 target.
*/
/// @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 _data Data to forward to L1 target.
function initiateWithdrawal(
address _target,
uint256 _gasLimit,
......@@ -128,13 +104,10 @@ contract L2ToL1MessagePasser is Semver {
}
}
/**
* @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
* different structures.
*
* @return Nonce of the next message to be sent, with added message version.
*/
/// @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
/// different structures.
/// @return Nonce of the next message to be sent, with added message version.
function messageNonce() public view returns (uint256) {
return Encoding.encodeVersionedNonce(msgNonce, MESSAGE_VERSION);
}
......
......@@ -4,33 +4,26 @@ pragma solidity 0.8.15;
import { Semver } from "../universal/Semver.sol";
import { FeeVault } from "../universal/FeeVault.sol";
/**
* @custom:proxied
* @custom:predeploy 0x4200000000000000000000000000000000000011
* @title SequencerFeeVault
* @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during
* transaction processing and block production.
*/
/// @custom:proxied
/// @custom:predeploy 0x4200000000000000000000000000000000000011
/// @title SequencerFeeVault
/// @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during
/// transaction processing and block production.
contract SequencerFeeVault is FeeVault, Semver {
/**
* @custom:semver 1.2.0
*
* @param _recipient Wallet that will receive the fees.
* @param _minWithdrawalAmount Minimum balance for withdrawals.
* @param _withdrawalNetwork Network which the recipient will receive fees on.
*/
/// @custom:semver 1.2.1
/// @notice Constructs the SequencerFeeVault contract.
/// @param _recipient Wallet that will receive the fees.
/// @param _minWithdrawalAmount Minimum balance for withdrawals.
/// @param _withdrawalNetwork Network which the recipient will receive fees on.
constructor(
address _recipient,
uint256 _minWithdrawalAmount,
WithdrawalNetwork _withdrawalNetwork
) FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) Semver(1, 2, 0) {}
) FeeVault(_recipient, _minWithdrawalAmount, _withdrawalNetwork) Semver(1, 2, 1) {}
/**
* @custom:legacy
* @notice Legacy getter for the recipient address.
*
* @return The recipient address.
*/
/// @custom:legacy
/// @notice Legacy getter for the recipient address.
/// @return The recipient address.
function l1FeeWallet() public view returns (address) {
return RECIPIENT;
}
......
......@@ -8,10 +8,10 @@ import { ClonesWithImmutableArgs } from "@cwia/ClonesWithImmutableArgs.sol";
import {
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { Semver } from "../universal/Semver.sol";
import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "./interfaces/IDisputeGameFactory.sol";
import { IVersioned } from "./interfaces/IVersioned.sol";
/// @title DisputeGameFactory
/// @notice A factory contract for creating `IDisputeGame` contracts. All created dispute games
......@@ -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
/// the dispute game. This is to make offchain discoverability of playable dispute games
/// easier.
contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersioned {
contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, Semver {
/// @dev Allows for the creation of clone proxies with immutable arguments.
using ClonesWithImmutableArgs for address;
......@@ -37,7 +37,7 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion
GameId[] internal _disputeGameList;
/// @notice constructs a new DisputeGameFactory contract.
constructor() OwnableUpgradeable() {
constructor() OwnableUpgradeable() Semver(0, 0, 2) {
initialize(address(0));
}
......@@ -48,12 +48,6 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion
_transferOwnership(_owner);
}
/// @inheritdoc IVersioned
/// @custom:semver 0.0.2
function version() external pure returns (string memory) {
return "0.0.2";
}
/// @inheritdoc IDisputeGameFactory
function gameCount() external view returns (uint256 gameCount_) {
gameCount_ = _disputeGameList.length;
......
......@@ -2,13 +2,13 @@
pragma solidity ^0.8.15;
import { IDisputeGame } from "./interfaces/IDisputeGame.sol";
import { IVersioned } from "./interfaces/IVersioned.sol";
import { IFaultDisputeGame } from "./interfaces/IFaultDisputeGame.sol";
import { IInitializable } from "./interfaces/IInitializable.sol";
import { IBondManager } from "./interfaces/IBondManager.sol";
import { IBigStepper } from "./interfaces/IBigStepper.sol";
import { Clone } from "../libraries/Clone.sol";
import { Semver } from "../universal/Semver.sol";
import { LibHashing } from "./lib/LibHashing.sol";
import { LibPosition } from "./lib/LibPosition.sol";
import { LibClock } from "./lib/LibClock.sol";
......@@ -18,7 +18,7 @@ import "../libraries/DisputeErrors.sol";
/// @title FaultDisputeGame
/// @notice An implementation of the `IFaultDisputeGame` interface.
contract FaultDisputeGame is IFaultDisputeGame, Clone {
contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
////////////////////////////////////////////////////////////////
// State Vars //
////////////////////////////////////////////////////////////////
......@@ -40,9 +40,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
/// @notice The root claim's position is always at gindex 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
Timestamp public gameStart;
......@@ -63,7 +60,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
Claim _absolutePrestate,
uint256 _maxGameDepth,
IBigStepper _vm
) {
) Semver(0, 0, 2) {
ABSOLUTE_PRESTATE = _absolutePrestate;
MAX_GAME_DEPTH = _maxGameDepth;
VM = _vm;
......@@ -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;
import "../../libraries/DisputeTypes.sol";
import { IVersioned } from "./IVersioned.sol";
import { IBondManager } from "./IBondManager.sol";
import { IInitializable } from "./IInitializable.sol";
/// @title IDisputeGame
/// @notice The generic interface for a DisputeGame contract.
interface IDisputeGame is IInitializable, IVersioned {
interface IDisputeGame is IInitializable {
/// @notice Emitted when the game is resolved.
/// @param status The status of the game after resolution.
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";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
/**
* @custom:predeploy 0x4200000000000000000000000000000000000042
* @title GovernanceToken
* @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
* permission to the `mint` function only, for the purposes of enforcing the token inflation
* schedule.
*/
/// @custom:predeploy 0x4200000000000000000000000000000000000042
/// @title GovernanceToken
/// @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
/// permission to the `mint` function only, for the purposes of enforcing the token
/// inflation schedule.
contract GovernanceToken is ERC20Burnable, ERC20Votes, Ownable {
/// @notice Constructs the GovernanceToken contract.
constructor() ERC20("Optimism", "OP") ERC20Permit("Optimism") {}
/**
* @notice Allows the owner to mint tokens.
*
* @param _account The account receiving minted tokens.
* @param _amount The amount of tokens to mint.
*/
/// @notice Allows the owner to mint tokens.
/// @param _account The account receiving minted tokens.
/// @param _amount The amount of tokens to mint.
function mint(address _account, uint256 _amount) public onlyOwner {
_mint(_account, _amount);
}
/**
* @notice Callback called after a token transfer.
*
* @param from The account sending tokens.
* @param to The account receiving tokens.
* @param amount The amount of tokens being transfered.
*/
/// @notice Callback called after a token transfer.
/// @param from The account sending tokens.
/// @param to The account receiving tokens.
/// @param amount The amount of tokens being transfered.
function _afterTokenTransfer(
address from,
address to,
......@@ -42,22 +35,16 @@ contract GovernanceToken is ERC20Burnable, ERC20Votes, Ownable {
super._afterTokenTransfer(from, to, amount);
}
/**
* @notice Internal mint function.
*
* @param to The account receiving minted tokens.
* @param amount The amount of tokens to mint.
*/
/// @notice Internal mint function.
/// @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) {
super._mint(to, amount);
}
/**
* @notice Internal burn function.
*
* @param account The account that tokens will be burned from.
* @param amount The amount of tokens that will be burned.
*/
/// @notice Internal burn function.
/// @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) {
super._burn(account, amount);
}
......
......@@ -4,57 +4,41 @@ pragma solidity 0.8.15;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./GovernanceToken.sol";
/**
* @title MintManager
* @notice Set as `owner` of the OP token and responsible for the token inflation schedule.
* Contract acts as the token "mint manager" with permission to the `mint` function only.
* Currently permitted to mint once per year of up to 2% of the total token supply.
* Upgradable to allow changes in the inflation schedule.
*/
/// @title MintManager
/// @notice Set as `owner` of the governance token and responsible for the token inflation
/// schedule. Contract acts as the token "mint manager" with permission to the `mint`
/// function only. Currently permitted to mint once per year of up to 2% of the total
/// token supply. Upgradable to allow changes in the inflation schedule.
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;
/**
* @notice The amount of tokens that can be minted per year. The value is a fixed
* point number with 4 decimals.
*/
/// @notice The amount of tokens that can be minted per year.
/// The value is a fixed point number with 4 decimals.
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;
/**
* @notice The amount of time that must pass before the MINT_CAP number of tokens can
* be minted again.
*/
/// @notice The amount of time that must pass before the MINT_CAP number of tokens can
/// be minted again.
uint256 public constant MINT_PERIOD = 365 days;
/**
* @notice Tracks the time of last mint.
*/
/// @notice Tracks the time of last mint.
uint256 public mintPermittedAfter;
/**
* @param _upgrader The owner of this contract
* @param _governanceToken The governance token this contract can mint
* tokens of
*/
/// @notice Constructs the MintManager contract.
/// @param _upgrader The owner of this contract.
/// @param _governanceToken The governance token this contract can mint tokens of.
constructor(address _upgrader, address _governanceToken) {
transferOwnership(_upgrader);
governanceToken = GovernanceToken(_governanceToken);
}
/**
* @notice Only the token owner is allowed to mint a certain amount of OP per year.
*
* @param _account Address to mint new tokens to.
* @param _amount Amount of tokens to be minted.
*/
/// @notice Only the token owner is allowed to mint a certain amount of the
/// governance token per year.
/// @param _account The account receiving minted tokens.
/// @param _amount The amount of tokens to mint.
function mint(address _account, uint256 _amount) public onlyOwner {
if (mintPermittedAfter > 0) {
require(
......@@ -72,11 +56,8 @@ contract MintManager is Ownable {
governanceToken.mint(_account, _amount);
}
/**
* @notice Upgrade the owner of the governance token to a new MintManager.
*
* @param _newMintManager The MintManager to upgrade to.
*/
/// @notice Upgrade the owner of the governance token to a new MintManager.
/// @param _newMintManager The MintManager to upgrade to.
function upgrade(address _newMintManager) public onlyOwner {
require(
_newMintManager != address(0),
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { CommonTest, Portal_Initializer } from "./CommonTest.t.sol";
import { CrossDomainOwnable } from "../L2/CrossDomainOwnable.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
// Testing utilities
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";
// Target contract dependencies
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
// Target contract
import { CrossDomainOwnable } from "../L2/CrossDomainOwnable.sol";
contract XDomainSetter is CrossDomainOwnable {
uint256 public value;
......@@ -23,13 +30,13 @@ contract CrossDomainOwnable_Test is CommonTest {
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 {
vm.expectRevert("CrossDomainOwnable: caller is not the owner");
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 {
assertEq(setter.value(), 0);
......@@ -42,6 +49,7 @@ contract CrossDomainOwnable_Test is CommonTest {
contract CrossDomainOwnableThroughPortal_Test is Portal_Initializer {
XDomainSetter setter;
/// @dev Sets up the test suite.
function setUp() public override {
super.setUp();
......@@ -49,6 +57,8 @@ contract CrossDomainOwnableThroughPortal_Test is Portal_Initializer {
setter = new XDomainSetter();
}
/// @dev Tests that `depositTransaction` succeeds when calling the `set` function on the
/// `XDomainSetter` contract.
function test_depositTransaction_crossDomainOwner_succeeds() external {
vm.recordLogs();
......
// SPDX-License-Identifier: MIT
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 { CrossDomainOwnable2 } from "../L2/CrossDomainOwnable2.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
// Libraries
import { Hashing } from "../libraries/Hashing.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 {
uint256 public value;
......@@ -19,17 +26,20 @@ contract XDomainSetter2 is CrossDomainOwnable2 {
contract CrossDomainOwnable2_Test is Messenger_Initializer {
XDomainSetter2 setter;
/// @dev Sets up the test suite.
function setUp() public override {
super.setUp();
vm.prank(alice);
setter = new XDomainSetter2();
}
/// @dev Tests that the `onlyOwner` modifier reverts when the caller is not the messenger.
function test_onlyOwner_notMessenger_reverts() external {
vm.expectRevert("CrossDomainOwnable2: caller is not the messenger");
setter.set(1);
}
/// @dev Tests that the `onlyOwner` modifier reverts when not called by the owner.
function test_onlyOwner_notOwner_reverts() external {
// set the xDomainMsgSender storage slot
bytes32 key = bytes32(uint256(204));
......@@ -41,6 +51,7 @@ contract CrossDomainOwnable2_Test is Messenger_Initializer {
setter.set(1);
}
/// @dev Tests that the `onlyOwner` modifier causes the relayed message to fail.
function test_onlyOwner_notOwner2_reverts() external {
uint240 nonce = 0;
address sender = bob;
......@@ -76,6 +87,7 @@ contract CrossDomainOwnable2_Test is Messenger_Initializer {
assertEq(setter.value(), 0);
}
/// @dev Tests that the `onlyOwner` modifier succeeds when called by the messenger.
function test_onlyOwner_succeeds() external {
address owner = setter.owner();
......
// SPDX-License-Identifier: MIT
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 { CrossDomainOwnable3 } from "../L2/CrossDomainOwnable3.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
// Libraries
import { Hashing } from "../libraries/Hashing.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 {
uint256 public value;
......@@ -19,43 +26,45 @@ contract XDomainSetter3 is CrossDomainOwnable3 {
contract CrossDomainOwnable3_Test is Messenger_Initializer {
XDomainSetter3 setter;
/**
* @notice OpenZeppelin Ownable.sol transferOwnership event
*/
/// @dev OpenZeppelin Ownable.sol transferOwnership event
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @notice CrossDomainOwnable3.sol transferOwnership event
*/
/// @dev CrossDomainOwnable3.sol transferOwnership event
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner,
bool isLocal
);
/// @dev Sets up the test suite.
function setUp() public override {
super.setUp();
vm.prank(alice);
setter = new XDomainSetter3();
}
/// @dev Tests that the constructor sets the correct variables.
function test_constructor_succeeds() public {
assertEq(setter.owner(), alice);
assertEq(setter.isLocal(), true);
}
/// @dev Tests that `set` reverts when the caller is not the owner.
function test_localOnlyOwner_notOwner_reverts() public {
vm.prank(bob);
vm.expectRevert("CrossDomainOwnable3: caller is not the owner");
setter.set(1);
}
/// @dev Tests that `transferOwnership` reverts when the caller is not the owner.
function test_transferOwnership_notOwner_reverts() public {
vm.prank(bob);
vm.expectRevert("CrossDomainOwnable3: caller is not the owner");
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 {
vm.expectEmit(true, true, true, true);
......@@ -78,6 +87,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
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 {
vm.expectEmit(true, true, true, true);
......@@ -126,6 +137,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
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 {
vm.expectEmit(true, true, true, true);
......@@ -143,18 +156,24 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
setter.set(1);
}
/// @dev Tests that `transferOwnership` reverts for ownership transfers
/// to the zero address when set locally.
function test_transferOwnership_zeroAddress_reverts() public {
vm.prank(setter.owner());
vm.expectRevert("CrossDomainOwnable3: new owner is the zero address");
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 {
vm.prank(setter.owner());
vm.expectRevert("Ownable: new owner is the zero address");
setter.transferOwnership(address(0));
}
/// @dev Tests that `onlyOwner` allows the owner to call a protected
/// function locally.
function test_localOnlyOwner_succeeds() public {
assertEq(setter.isLocal(), true);
vm.prank(setter.owner());
......@@ -162,6 +181,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
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 {
vm.expectEmit(true, true, true, true, address(setter));
emit OwnershipTransferred(alice, bob);
......@@ -177,10 +198,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
assertEq(setter.value(), 2);
}
/**
* @notice The existing transferOwnership(address) method
* still exists on the contract
*/
/// @dev The existing transferOwnership(address) method still
/// exists on the contract.
function test_transferOwnershipNoLocal_succeeds() public {
bool isLocal = setter.isLocal();
......@@ -198,6 +217,8 @@ contract CrossDomainOwnable3_Test is Messenger_Initializer {
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 {
vm.expectEmit(true, true, true, true, address(setter));
emit OwnershipTransferred(alice, bob);
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
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 { StandardBridge } from "../universal/StandardBridge.sol";
// Libraries
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
contract FeeVault_Test is Bridge_Initializer {
BaseFeeVault baseFeeVault = BaseFeeVault(payable(Predeploys.BASE_FEE_VAULT));
......@@ -16,6 +22,7 @@ contract FeeVault_Test is Bridge_Initializer {
uint256 constant otherMinimumWithdrawalAmount = 10 ether;
/// @dev Sets up the test suite.
function setUp() public override {
super.setUp();
vm.etch(
......@@ -33,6 +40,7 @@ contract FeeVault_Test is Bridge_Initializer {
vm.label(Predeploys.L1_FEE_VAULT, "L1FeeVault");
}
/// @dev Tests that the constructor sets the correct values.
function test_constructor_succeeds() external {
assertEq(baseFeeVault.RECIPIENT(), alice);
assertEq(l1FeeVault.RECIPIENT(), bob);
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "./CommonTest.t.sol";
import { GasPriceOracle } from "../L2/GasPriceOracle.sol";
// Target contract dependencies
import { L1Block } from "../L2/L1Block.sol";
import { Predeploys } from "../libraries/Predeploys.sol";
// Target contract
import { GasPriceOracle } from "../L2/GasPriceOracle.sol";
contract GasPriceOracle_Test is CommonTest {
event OverheadUpdated(uint256);
event ScalarUpdated(uint256);
......@@ -15,7 +20,7 @@ contract GasPriceOracle_Test is CommonTest {
L1Block l1Block;
address depositor;
// set the initial L1 context values
// The initial L1 context values
uint64 constant number = 10;
uint64 constant timestamp = 11;
uint256 constant basefee = 100;
......@@ -25,6 +30,7 @@ contract GasPriceOracle_Test is CommonTest {
uint256 constant l1FeeOverhead = 310;
uint256 constant l1FeeScalar = 10;
/// @dev Sets up the test suite.
function setUp() public virtual override {
super.setUp();
// place the L1Block contract at the predeploy address
......@@ -51,36 +57,42 @@ contract GasPriceOracle_Test is CommonTest {
});
}
/// @dev Tests that `l1BaseFee` is set correctly.
function test_l1BaseFee_succeeds() external {
assertEq(gasOracle.l1BaseFee(), basefee);
}
/// @dev Tests that `gasPrice` is set correctly.
function test_gasPrice_succeeds() external {
vm.fee(100);
uint256 gasPrice = gasOracle.gasPrice();
assertEq(gasPrice, 100);
}
/// @dev Tests that `baseFee` is set correctly.
function test_baseFee_succeeds() external {
vm.fee(64);
uint256 gasPrice = gasOracle.baseFee();
assertEq(gasPrice, 64);
}
/// @dev Tests that `scalar` is set correctly.
function test_scalar_succeeds() external {
assertEq(gasOracle.scalar(), l1FeeScalar);
}
/// @dev Tests that `overhead` is set correctly.
function test_overhead_succeeds() external {
assertEq(gasOracle.overhead(), l1FeeOverhead);
}
/// @dev Tests that `decimals` is set correctly.
function test_decimals_succeeds() external {
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 {
(bool success, bytes memory returndata) = address(gasOracle).call(
abi.encodeWithSignature("setGasPrice(uint256)", 1)
......@@ -90,7 +102,7 @@ contract GasPriceOracle_Test is CommonTest {
assertEq(returndata, hex"");
}
// Removed in bedrock
/// @dev Tests that `setL1BaseFee` reverts since it was removed in bedrock.
function test_setL1BaseFee_doesNotExist_reverts() external {
(bool success, bytes memory returndata) = address(gasOracle).call(
abi.encodeWithSignature("setL1BaseFee(uint256)", 1)
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "./CommonTest.t.sol";
// Target contract
import { GovernanceToken } from "../governance/GovernanceToken.sol";
contract GovernanceToken_Test is CommonTest {
......@@ -9,12 +12,14 @@ contract GovernanceToken_Test is CommonTest {
address constant rando = address(0x5678);
GovernanceToken internal gov;
/// @dev Sets up the test suite.
function setUp() public virtual override {
super.setUp();
vm.prank(owner);
gov = new GovernanceToken();
}
/// @dev Tests that the constructor sets the correct initial state.
function test_constructor_succeeds() external {
assertEq(gov.owner(), owner);
assertEq(gov.name(), "Optimism");
......@@ -23,6 +28,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 0);
}
/// @dev Tests that the owner can successfully call `mint`.
function test_mint_fromOwner_succeeds() external {
// Mint 100 tokens.
vm.prank(owner);
......@@ -33,6 +39,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 100);
}
/// @dev Tests that `mint` reverts when called by a non-owner.
function test_mint_fromNotOwner_reverts() external {
// Mint 100 tokens as rando.
vm.prank(rando);
......@@ -44,6 +51,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 0);
}
/// @dev Tests that the owner can successfully call `burn`.
function test_burn_succeeds() external {
// Mint 100 tokens to rando.
vm.prank(owner);
......@@ -58,6 +66,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 50);
}
/// @dev Tests that the owner can successfully call `burnFrom`.
function test_burnFrom_succeeds() external {
// Mint 100 tokens to rando.
vm.prank(owner);
......@@ -76,6 +85,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 50);
}
/// @dev Tests that `transfer` correctly transfers tokens.
function test_transfer_succeeds() external {
// Mint 100 tokens to rando.
vm.prank(owner);
......@@ -91,6 +101,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 100);
}
/// @dev Tests that `approve` correctly sets allowances.
function test_approve_succeeds() external {
// Mint 100 tokens to rando.
vm.prank(owner);
......@@ -104,6 +115,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.allowance(rando, owner), 50);
}
/// @dev Tests that `transferFrom` correctly transfers tokens.
function test_transferFrom_succeeds() external {
// Mint 100 tokens to rando.
vm.prank(owner);
......@@ -123,6 +135,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.totalSupply(), 100);
}
/// @dev Tests that `increaseAllowance` correctly increases allowances.
function test_increaseAllowance_succeeds() external {
// Mint 100 tokens to rando.
vm.prank(owner);
......@@ -140,6 +153,7 @@ contract GovernanceToken_Test is CommonTest {
assertEq(gov.allowance(rando, owner), 100);
}
/// @dev Tests that `decreaseAllowance` correctly decreases allowances.
function test_decreaseAllowance_succeeds() external {
// Mint 100 tokens to rando.
vm.prank(owner);
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "./CommonTest.t.sol";
// Target contract
import { L1Block } from "../L2/L1Block.sol";
contract L1BlockTest is CommonTest {
......@@ -9,6 +12,7 @@ contract L1BlockTest is CommonTest {
address depositor;
bytes32 immutable NON_ZERO_HASH = keccak256(abi.encode(1));
/// @dev Sets up the test suite.
function setUp() public virtual override {
super.setUp();
lb = new L1Block();
......@@ -26,6 +30,7 @@ contract L1BlockTest is CommonTest {
});
}
/// @dev Tests that `setL1BlockValues` updates the values correctly.
function testFuzz_updatesValues_succeeds(
uint64 n,
uint64 t,
......@@ -48,26 +53,32 @@ contract L1BlockTest is CommonTest {
assertEq(lb.l1FeeScalar(), fs);
}
/// @dev Tests that `number` returns the correct value.
function test_number_succeeds() external {
assertEq(lb.number(), uint64(1));
}
/// @dev Tests that `timestamp` returns the correct value.
function test_timestamp_succeeds() external {
assertEq(lb.timestamp(), uint64(2));
}
/// @dev Tests that `basefee` returns the correct value.
function test_basefee_succeeds() external {
assertEq(lb.basefee(), 3);
}
/// @dev Tests that `hash` returns the correct value.
function test_hash_succeeds() external {
assertEq(lb.hash(), NON_ZERO_HASH);
}
/// @dev Tests that `sequenceNumber` returns the correct value.
function test_sequenceNumber_succeeds() external {
assertEq(lb.sequenceNumber(), uint64(4));
}
/// @dev Tests that `setL1BlockValues` can set max values.
function test_updateValues_succeeds() external {
vm.prank(depositor);
lb.setL1BlockValues({
......
......@@ -25,7 +25,6 @@ contract L1StandardBridge_Getter_Test is Bridge_Initializer {
assert(L1Bridge.OTHER_BRIDGE() == L2Bridge);
assert(L1Bridge.messenger() == L1Messenger);
assert(L1Bridge.MESSENGER() == L1Messenger);
assertEq(L1Bridge.version(), "1.1.1");
}
}
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { Messenger_Initializer, Reverter, ConfigurableCaller } from "./CommonTest.t.sol";
import { AddressAliasHelper } from "../vendor/AddressAliasHelper.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2CrossDomainMessenger } from "../L2/L2CrossDomainMessenger.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
// Libraries
import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.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 {
// Receiver address for testing
/// @dev Receiver address for testing
address recipient = address(0xabbaacdc);
/// @dev Tests that `messageNonce` can be decoded correctly.
function test_messageVersion_succeeds() external {
(, uint16 version) = Encoding.decodeVersionedNonce(L2Messenger.messageNonce());
assertEq(version, L2Messenger.MESSAGE_VERSION());
}
/// @dev Tests that `sendMessage` executes successfully.
function test_sendMessage_succeeds() external {
bytes memory xDomainCallData = Encoding.encodeCrossDomainMessage(
L2Messenger.messageNonce(),
......@@ -65,6 +72,8 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
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 {
uint256 nonce = L2Messenger.messageNonce();
L2Messenger.sendMessage(recipient, hex"aa", uint32(500_000));
......@@ -73,11 +82,13 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
assertEq(nonce + 2, L2Messenger.messageNonce());
}
/// @dev Tests that `sendMessage` reverts if the recipient is the zero address.
function test_xDomainSender_senderNotSet_reverts() external {
vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender();
}
/// @dev Tests that `sendMessage` reverts if the message version is not supported.
function test_relayMessage_v2_reverts() external {
address target = address(0xabcd);
address sender = address(L1Messenger);
......@@ -100,6 +111,7 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
);
}
/// @dev Tests that `relayMessage` executes successfully.
function test_relayMessage_succeeds() external {
address target = address(0xabcd);
address sender = address(L1Messenger);
......@@ -137,7 +149,8 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
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 {
address target = address(messagePasser);
address sender = address(L1Messenger);
......@@ -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 {
vm.expectRevert("CrossDomainMessenger: xDomainMessageSender is not set");
L2Messenger.xDomainMessageSender();
......@@ -176,8 +190,9 @@ contract L2CrossDomainMessenger_Test is Messenger_Initializer {
L2Messenger.xDomainMessageSender();
}
// relayMessage: should send a successful call to the target contract after the first message
// fails and ETH gets stuck, but the second message succeeds
/// @dev Tests that `relayMessage` is able to send a successful call
/// to the target contract after the first message fails and ETH
/// gets stuck, but the second message succeeds.
function test_relayMessage_retry_succeeds() external {
address target = address(0xabcd);
address sender = address(L1Messenger);
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
// Testing utilities
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 { L2ERC721Bridge } from "../L2/L2ERC721Bridge.sol";
import { OptimismMintableERC721 } from "../universal/OptimismMintableERC721.sol";
// Target contract
import { L2ERC721Bridge } from "../L2/L2ERC721Bridge.sol";
contract TestERC721 is ERC721 {
constructor() ERC721("Test", "TST") {}
......@@ -50,6 +55,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
bytes extraData
);
/// @dev Sets up the test suite.
function setUp() public override {
super.setUp();
......@@ -69,6 +75,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
localToken.approve(address(bridge), tokenId);
}
/// @dev Tests that the constructor sets the correct variables.
function test_constructor_succeeds() public {
assertEq(address(bridge.MESSENGER()), address(L2Messenger));
assertEq(address(bridge.OTHER_BRIDGE()), otherBridge);
......@@ -76,6 +83,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
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 {
// Expect a call to the messenger.
vm.expectCall(
......@@ -120,6 +129,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
localToken.ownerOf(tokenId);
}
/// @dev Tests that `bridgeERC721` reverts if the owner is not an EOA.
function test_bridgeERC721_fromContract_reverts() external {
// Bridge the token.
vm.etch(alice, hex"01");
......@@ -131,6 +141,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice);
}
/// @dev Tests that `bridgeERC721` reverts if the local token is the zero address.
function test_bridgeERC721_localTokenZeroAddress_reverts() external {
// Bridge the token.
vm.prank(alice);
......@@ -141,6 +152,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice);
}
/// @dev Tests that `bridgeERC721` reverts if the remote token is the zero address.
function test_bridgeERC721_remoteTokenZeroAddress_reverts() external {
// Bridge the token.
vm.prank(alice);
......@@ -151,6 +163,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice);
}
/// @dev Tests that `bridgeERC721` reverts if the caller is not the token owner.
function test_bridgeERC721_wrongOwner_reverts() external {
// Bridge the token.
vm.prank(bob);
......@@ -161,6 +174,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
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 {
// Expect a call to the messenger.
vm.expectCall(
......@@ -205,6 +220,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
localToken.ownerOf(tokenId);
}
/// @dev Tests that `bridgeERC721To` reverts if the local token is the zero address.
function test_bridgeERC721To_localTokenZeroAddress_reverts() external {
// Bridge the token.
vm.prank(alice);
......@@ -215,6 +231,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice);
}
/// @dev Tests that `bridgeERC721To` reverts if the remote token is the zero address.
function test_bridgeERC721To_remoteTokenZeroAddress_reverts() external {
// Bridge the token.
vm.prank(alice);
......@@ -225,6 +242,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice);
}
/// @dev Tests that `bridgeERC721To` reverts if the caller is not the token owner.
function test_bridgeERC721To_wrongOwner_reverts() external {
// Bridge the token.
vm.prank(bob);
......@@ -242,6 +260,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
assertEq(localToken.ownerOf(tokenId), alice);
}
/// @dev Tests that `finalizeBridgeERC721` correctly finalizes a bridged token.
function test_finalizeBridgeERC721_succeeds() external {
// Bridge the token.
vm.prank(alice);
......@@ -278,6 +297,8 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
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 {
// Create a non-compliant token
NonCompliantERC721 nonCompliantToken = new NonCompliantERC721(alice);
......@@ -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 {
// Finalize a withdrawal.
vm.prank(alice);
......@@ -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 {
// Finalize a withdrawal.
vm.mockCall(
......@@ -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 {
// Finalize a withdrawal.
vm.mockCall(
......@@ -357,6 +382,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
);
}
/// @dev Tests that `finalizeBridgeERC721` reverts when already finalized.
function test_finalizeBridgeERC721_alreadyExists_reverts() external {
// Finalize a withdrawal.
vm.mockCall(
......@@ -377,12 +403,9 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
}
}
/**
* @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.
*/
/// @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.
contract NonCompliantERC721 {
address internal immutable owner;
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
// Target contract is imported by the `Bridge_Initializer`
import { Bridge_Initializer } from "./CommonTest.t.sol";
import { stdStorage, StdStorage } from "forge-std/Test.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 { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// Libraries
import { Hashing } from "../libraries/Hashing.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";
contract L2StandardBridge_Test is Bridge_Initializer {
using stdStorage for StdStorage;
/// @dev Tests that the bridge is initialized correctly.
function test_initialize_succeeds() external {
assertEq(address(L2Bridge.messenger()), address(L2Messenger));
assertEq(L1Bridge.l2TokenBridge(), address(L2Bridge));
assertEq(address(L2Bridge.OTHER_BRIDGE()), address(L1Bridge));
}
// receive
// - can accept ETH
/// @dev Tests that the bridge receives ETH and successfully initiates a withdrawal.
function test_receive_succeeds() external {
assertEq(address(messagePasser).balance, 0);
uint256 nonce = L2Messenger.messageNonce();
......@@ -109,8 +113,7 @@ contract L2StandardBridge_Test is Bridge_Initializer {
assertEq(address(messagePasser).balance, 100);
}
// withrdraw
// - requires amount == msg.value
/// @dev Tests that `withdraw` reverts if the amount is not equal to the value sent.
function test_withdraw_insufficientValue_reverts() external {
assertEq(address(messagePasser).balance, 0);
......@@ -119,10 +122,8 @@ contract L2StandardBridge_Test is Bridge_Initializer {
L2Bridge.withdraw(address(Predeploys.LEGACY_ERC20_ETH), 100, 1000, hex"");
}
/**
* @notice Use the legacy `withdraw` interface on the L2StandardBridge to
* withdraw ether from L2 to L1.
*/
/// @dev Tests that the legacy `withdraw` interface on the L2StandardBridge
/// successfully initiates a withdrawal.
function test_withdraw_ether_succeeds() external {
assertTrue(alice.balance >= 100);
assertEq(Predeploys.L2_TO_L1_MESSAGE_PASSER.balance, 0);
......@@ -153,8 +154,7 @@ contract L2StandardBridge_Test is Bridge_Initializer {
}
contract PreBridgeERC20 is Bridge_Initializer {
// withdraw and BridgeERC20 should behave the same when transferring ERC20 tokens
// so they should share the same setup and expectEmit calls
/// @dev Sets up expected calls and emits for a successful ERC20 withdrawal.
function _preBridgeERC20(bool _isLegacy, address _l2Token) internal {
// Alice has 100 L2Token
deal(_l2Token, alice, 100, true);
......@@ -430,10 +430,8 @@ contract PreBridgeERC20To is Bridge_Initializer {
}
contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To {
// withdrawTo
// - token is burned
// - emits WithdrawalInitiated w/ correct recipient
// - calls Withdrawer.initiateWithdrawal
/// @dev Tests that `withdrawTo` burns the tokens, emits `WithdrawalInitiated`,
/// and initiates a withdrawal with `Withdrawer.initiateWithdrawal`.
function test_withdrawTo_withdrawingERC20_succeeds() external {
_preBridgeERC20To({ _isLegacy: true, _l2Token: address(L2Token) });
L2Bridge.withdrawTo(address(L2Token), bob, 100, 1000, hex"");
......@@ -441,10 +439,8 @@ contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To {
assertEq(L2Token.balanceOf(alice), 0);
}
// bridgeERC20To
// - token is burned
// - emits WithdrawalInitiated w/ correct recipient
// - calls Withdrawer.initiateWithdrawal
/// @dev Tests that `bridgeERC20To` burns the tokens, emits `WithdrawalInitiated`,
/// and initiates a withdrawal with `Withdrawer.initiateWithdrawal`.
function test_bridgeERC20To_succeeds() external {
_preBridgeERC20To({ _isLegacy: false, _l2Token: address(L2Token) });
L2Bridge.bridgeERC20To(address(L2Token), address(L1Token), bob, 100, 1000, hex"");
......@@ -453,10 +449,10 @@ contract L2StandardBridge_BridgeERC20To_Test is PreBridgeERC20To {
}
contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
// finalizeDeposit
// - only callable by l1TokenBridge
// - supported token pair emits DepositFinalized
// - invalid deposit calls Withdrawer.initiateWithdrawal
/// @dev Tests that `finalizeDeposit` succeeds. It should:
/// - only be callable by the l1TokenBridge
/// - emit `DepositFinalized` if the token pair is supported
/// - call `Withdrawer.initiateWithdrawal` if the token pair is not supported
function test_finalizeDeposit_depositingERC20_succeeds() external {
vm.mockCall(
address(L2Bridge.messenger()),
......@@ -480,6 +476,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
L2Bridge.finalizeDeposit(address(L1Token), address(L2Token), alice, alice, 100, hex"");
}
/// @dev Tests that `finalizeDeposit` succeeds when depositing ETH.
function test_finalizeDeposit_depositingETH_succeeds() external {
vm.mockCall(
address(L2Bridge.messenger()),
......@@ -505,6 +502,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
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 {
vm.mockCall(
address(L2Bridge.messenger()),
......@@ -517,6 +515,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
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 {
vm.mockCall(
address(L2Bridge.messenger()),
......@@ -529,6 +528,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
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 {
vm.mockCall(
address(L2Bridge.messenger()),
......@@ -543,6 +543,7 @@ contract L2StandardBridge_Bridge_Test is Bridge_Initializer {
}
contract L2StandardBridge_FinalizeBridgeETH_Test is Bridge_Initializer {
/// @dev Tests that `finalizeBridgeETH` succeeds.
function test_finalizeBridgeETH_succeeds() external {
address messenger = address(L2Bridge.messenger());
vm.mockCall(
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "./CommonTest.t.sol";
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
// Libraries
import { Types } from "../libraries/Types.sol";
import { Hashing } from "../libraries/Hashing.sol";
// Target contract
import { L2ToL1MessagePasser } from "../L2/L2ToL1MessagePasser.sol";
contract L2ToL1MessagePasserTest is CommonTest {
L2ToL1MessagePasser messagePasser;
......@@ -21,11 +26,14 @@ contract L2ToL1MessagePasserTest is CommonTest {
event WithdrawerBalanceBurnt(uint256 indexed amount);
/// @dev Sets up the test suite.
function setUp() public virtual override {
super.setUp();
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(
address _sender,
address _target,
......@@ -60,7 +68,8 @@ contract L2ToL1MessagePasserTest is CommonTest {
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 {
bytes32 withdrawalHash = Hashing.hashWithdrawal(
Types.WithdrawalTransaction(
......@@ -88,7 +97,8 @@ contract L2ToL1MessagePasserTest is CommonTest {
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 {
uint256 gasLimit = 64000;
address target = address(4);
......@@ -114,7 +124,7 @@ contract L2ToL1MessagePasserTest is CommonTest {
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 {
messagePasser.initiateWithdrawal{ value: NON_ZERO_VALUE }(
NON_ZERO_ADDRESS,
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
import { CommonTest } from "./CommonTest.t.sol";
import { MintManager } from "../governance/MintManager.sol";
// Target contract dependencies
import { GovernanceToken } from "../governance/GovernanceToken.sol";
// Target contract
import { MintManager } from "../governance/MintManager.sol";
contract MintManager_Initializer is CommonTest {
address constant owner = address(0x1234);
address constant rando = address(0x5678);
GovernanceToken internal gov;
MintManager internal manager;
/// @dev Sets up the test suite.
function setUp() public virtual override {
super.setUp();
......@@ -26,9 +32,7 @@ contract MintManager_Initializer is CommonTest {
}
contract MintManager_constructor_Test is MintManager_Initializer {
/**
* @notice Tests that the constructor properly configures the contract.
*/
/// @dev Tests that the constructor properly configures the contract.
function test_constructor_succeeds() external {
assertEq(manager.owner(), owner);
assertEq(address(manager.governanceToken()), address(gov));
......@@ -36,9 +40,7 @@ contract MintManager_constructor_Test is MintManager_Initializer {
}
contract MintManager_mint_Test is MintManager_Initializer {
/**
* @notice Tests that the mint function properly mints tokens when called by the owner.
*/
/// @dev Tests that the mint function properly mints tokens when called by the owner.
function test_mint_fromOwner_succeeds() external {
// Mint once.
vm.prank(owner);
......@@ -48,9 +50,7 @@ contract MintManager_mint_Test is MintManager_Initializer {
assertEq(gov.balanceOf(owner), 100);
}
/**
* @notice Tests that the mint function reverts when called by a non-owner.
*/
/// @dev Tests that the mint function reverts when called by a non-owner.
function test_mint_fromNotOwner_reverts() external {
// Mint from rando fails.
vm.prank(rando);
......@@ -58,10 +58,8 @@ contract MintManager_mint_Test is MintManager_Initializer {
manager.mint(owner, 100);
}
/**
* @notice Tests that the mint function properly mints tokens when called by the owner a second
* time after the mint period has elapsed.
*/
/// @dev Tests that the mint function properly mints tokens when called by the owner a second
/// time after the mint period has elapsed.
function test_mint_afterPeriodElapsed_succeeds() external {
// Mint once.
vm.prank(owner);
......@@ -79,10 +77,8 @@ contract MintManager_mint_Test is MintManager_Initializer {
assertEq(gov.balanceOf(owner), 102);
}
/**
* @notice Tests that the mint function always reverts when called before the mint period has
* elapsed, even if the caller is the owner.
*/
/// @dev Tests that the mint function always reverts when called before the mint period has
/// elapsed, even if the caller is the owner.
function test_mint_beforePeriodElapsed_reverts() external {
// Mint once.
vm.prank(owner);
......@@ -100,9 +96,7 @@ contract MintManager_mint_Test is MintManager_Initializer {
assertEq(gov.balanceOf(owner), 100);
}
/**
* @notice Tests that the owner cannot mint more than the mint cap.
*/
/// @dev Tests that the owner cannot mint more than the mint cap.
function test_mint_moreThanCap_reverts() external {
// Mint once.
vm.prank(owner);
......@@ -123,9 +117,7 @@ contract MintManager_mint_Test is MintManager_Initializer {
}
contract MintManager_upgrade_Test is MintManager_Initializer {
/**
* @notice Tests that the owner can upgrade the mint manager.
*/
/// @dev Tests that the owner can upgrade the mint manager.
function test_upgrade_fromOwner_succeeds() external {
// Upgrade to new manager.
vm.prank(owner);
......@@ -135,9 +127,7 @@ contract MintManager_upgrade_Test is MintManager_Initializer {
assertEq(gov.owner(), rando);
}
/**
* @notice Tests that the upgrade function reverts when called by a non-owner.
*/
/// @dev Tests that the upgrade function reverts when called by a non-owner.
function test_upgrade_fromNotOwner_reverts() external {
// Upgrade from rando fails.
vm.prank(rando);
......@@ -145,10 +135,8 @@ contract MintManager_upgrade_Test is MintManager_Initializer {
manager.upgrade(rando);
}
/**
* @notice Tests that the upgrade function reverts when attempting to update to the zero
* address, even if the caller is the owner.
*/
/// @dev Tests that the upgrade function reverts when attempting to update to the zero
/// address, even if the caller is the owner.
function test_upgrade_toZeroAddress_reverts() external {
// Upgrade to zero address fails.
vm.prank(owner);
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
// Testing utilities
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";
// Target contract
import { SequencerFeeVault } from "../L2/SequencerFeeVault.sol";
import { StandardBridge } from "../universal/StandardBridge.sol";
import { Predeploys } from "../libraries/Predeploys.sol";
contract SequencerFeeVault_Test is FeeVault_Initializer {
/// @dev Sets up the test suite.
function setUp() public override {
super.setUp();
vm.etch(
......@@ -19,14 +26,17 @@ contract SequencerFeeVault_Test is FeeVault_Initializer {
vm.label(Predeploys.SEQUENCER_FEE_WALLET, "SequencerFeeVault");
}
/// @dev Tests that the minimum withdrawal amount is correct.
function test_minWithdrawalAmount_succeeds() external {
assertEq(vault.MIN_WITHDRAWAL_AMOUNT(), NON_ZERO_VALUE);
}
/// @dev Tests that the l1 fee wallet is correct.
function test_constructor_succeeds() external {
assertEq(vault.l1FeeWallet(), recipient);
}
/// @dev Tests that the fee vault is able to receive ETH.
function test_receive_succeeds() external {
uint256 balance = address(vault).balance;
......@@ -37,6 +47,8 @@ contract SequencerFeeVault_Test is FeeVault_Initializer {
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 {
assert(address(vault).balance < vault.MIN_WITHDRAWAL_AMOUNT());
......@@ -46,6 +58,7 @@ contract SequencerFeeVault_Test is FeeVault_Initializer {
vault.withdraw();
}
/// @dev Tests that `withdraw` successfully initiates a withdrawal to L1.
function test_withdraw_toL1_succeeds() external {
uint256 amount = vault.MIN_WITHDRAWAL_AMOUNT() + 1;
vm.deal(address(vault), amount);
......@@ -85,6 +98,7 @@ contract SequencerFeeVault_Test is FeeVault_Initializer {
}
contract SequencerFeeVault_L2Withdrawal_Test is FeeVault_Initializer {
/// @dev Sets up the test suite.
function setUp() public override {
super.setUp();
vm.etch(
......@@ -95,6 +109,7 @@ contract SequencerFeeVault_L2Withdrawal_Test is FeeVault_Initializer {
vm.label(Predeploys.SEQUENCER_FEE_WALLET, "SequencerFeeVault");
}
/// @dev Tests that `withdraw` successfully initiates a withdrawal to L2.
function test_withdraw_toL2_succeeds() external {
uint256 amount = vault.MIN_WITHDRAWAL_AMOUNT() + 1;
vm.deal(address(vault), amount);
......
......@@ -10,6 +10,7 @@
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"l2OutputOracleSubmissionInterval": 20,
"l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65",
"l2GenesisBlockGasLimit": "0x1c9c380",
......@@ -40,7 +41,7 @@
"governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096",
"eip1559Denominator": 8,
"eip1559Elasticity": 2,
"l1GenesisBlockTimestamp": "0x648a0943",
"l1GenesisBlockTimestamp": "0x64935846",
"l1StartingBlockTag": "earliest",
"l2GenesisRegolithTimeOffset": "0x0"
}
\ No newline at end of file
......@@ -16,6 +16,8 @@
"l2OutputOracleSubmissionInterval": 6,
"l2OutputOracleStartingTimestamp": 0,
"l2OutputOracleStartingBlockNumber": 0,
"gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x6925B8704Ff96DEe942623d6FB5e946EF5884b63",
"l2GenesisBlockBaseFeePerGas": "0x3B9ACA00",
......@@ -34,5 +36,8 @@
"governanceTokenSymbol": "OP",
"governanceTokenOwner": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"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'
fs_permissions = [
{ '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]
......
......@@ -26,7 +26,6 @@
"build:fuzz": "(cd test-case-generator && go build ./cmd/fuzz.go)",
"autogen:artifacts": "ts-node scripts/generate-artifacts.ts",
"autogen:invariant-docs": "ts-node scripts/invariant-doc-gen.ts",
"deploy": "hardhat deploy",
"test": "yarn build:differential && yarn build:fuzz && forge test",
"coverage": "yarn build:differential && yarn build:fuzz && forge coverage",
"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 = {
await assertSemver(
L2CrossDomainMessenger,
'L2CrossDomainMessenger',
'1.4.0'
'1.4.1'
)
const xDomainMessageSenderSlot = await signer.provider.getStorageAt(
......@@ -351,7 +351,7 @@ const check = {
signer
)
await assertSemver(GasPriceOracle, 'GasPriceOracle')
await assertSemver(GasPriceOracle, 'GasPriceOracle', '1.0.1')
const decimals = await GasPriceOracle.decimals()
assert(decimals.eq(6))
......@@ -369,7 +369,7 @@ const check = {
signer
)
await assertSemver(L2StandardBridge, 'L2StandardBridge', '1.1.0')
await assertSemver(L2StandardBridge, 'L2StandardBridge', '1.1.1')
const OTHER_BRIDGE = await L2StandardBridge.OTHER_BRIDGE()
assert(OTHER_BRIDGE !== hre.ethers.constants.AddressZero)
......@@ -392,7 +392,7 @@ const check = {
signer
)
await assertSemver(SequencerFeeVault, 'SequencerFeeVault', '1.2.0')
await assertSemver(SequencerFeeVault, 'SequencerFeeVault', '1.2.1')
const RECIPIENT = await SequencerFeeVault.RECIPIENT()
assert(RECIPIENT !== hre.ethers.constants.AddressZero)
......@@ -460,7 +460,7 @@ const check = {
signer
)
await assertSemver(L1Block, 'L1Block')
await assertSemver(L1Block, 'L1Block', '1.0.1')
await checkProxy(hre, 'L1Block', signer.provider)
await assertProxy(hre, 'L1Block', signer.provider)
......@@ -526,7 +526,7 @@ const check = {
signer
)
await assertSemver(L2ERC721Bridge, 'L2ERC721Bridge', '1.1.0')
await assertSemver(L2ERC721Bridge, 'L2ERC721Bridge', '1.1.1')
const MESSENGER = await L2ERC721Bridge.MESSENGER()
assert(MESSENGER !== hre.ethers.constants.AddressZero)
......@@ -599,7 +599,7 @@ const check = {
signer
)
await assertSemver(BaseFeeVault, 'BaseFeeVault', '1.2.0')
await assertSemver(BaseFeeVault, 'BaseFeeVault', '1.2.1')
const MIN_WITHDRAWAL_AMOUNT = await BaseFeeVault.MIN_WITHDRAWAL_AMOUNT()
console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`)
......@@ -626,7 +626,7 @@ const check = {
signer
)
await assertSemver(L1FeeVault, 'L1FeeVault', '1.2.0')
await assertSemver(L1FeeVault, 'L1FeeVault', '1.2.1')
const MIN_WITHDRAWAL_AMOUNT = await L1FeeVault.MIN_WITHDRAWAL_AMOUNT()
console.log(` - MIN_WITHDRAWAL_AMOUNT: ${MIN_WITHDRAWAL_AMOUNT}`)
......@@ -654,7 +654,7 @@ const check = {
signer
)
await assertSemver(L2ToL1MessagePasser, 'L2ToL1MessagePasser')
await assertSemver(L2ToL1MessagePasser, 'L2ToL1MessagePasser', '1.0.1')
const MESSAGE_VERSION = await L2ToL1MessagePasser.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