Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/contracts-bedrock/scripts/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ library Config {

/// @notice Returns the path that the state dump file should be written to or read from
/// on the local filesystem.
function stateDumpPath(string memory _name) internal view returns (string memory _env) {
function stateDumpPath() internal view returns (string memory _env) {
_env = vm.envOr(
"STATE_DUMP_PATH", string.concat(vm.projectRoot(), "/", _name, "-", vm.toString(block.chainid), ".json")
"STATE_DUMP_PATH", string.concat(vm.projectRoot(), "/state-dump-", vm.toString(block.chainid), ".json")
);
}

Expand Down
3 changes: 1 addition & 2 deletions packages/contracts-bedrock/scripts/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,7 @@ contract Deploy is Deployer {

function runWithStateDump() public {
_run();

vm.dumpState(Config.stateDumpPath(name()));
vm.dumpState(Config.stateDumpPath());
}

/// @notice Deploy all L1 contracts and write the state diff to a file.
Expand Down
4 changes: 4 additions & 0 deletions packages/contracts-bedrock/scripts/DeployConfig.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ contract DeployConfig is Script {
address public proxyAdminOwner;
address public baseFeeVaultRecipient;
uint256 public baseFeeVaultMinimumWithdrawalAmount;
uint256 public baseFeeVaultWithdrawalNetwork;
address public l1FeeVaultRecipient;
uint256 public l1FeeVaultMinimumWithdrawalAmount;
uint256 public l1FeeVaultWithdrawalNetwork;
address public sequencerFeeVaultRecipient;
uint256 public sequencerFeeVaultMinimumWithdrawalAmount;
uint256 public sequencerFeeVaultWithdrawalNetwork;
Expand Down Expand Up @@ -106,8 +108,10 @@ contract DeployConfig is Script {
proxyAdminOwner = stdJson.readAddress(_json, "$.proxyAdminOwner");
baseFeeVaultRecipient = stdJson.readAddress(_json, "$.baseFeeVaultRecipient");
baseFeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.baseFeeVaultMinimumWithdrawalAmount");
baseFeeVaultWithdrawalNetwork = stdJson.readUint(_json, "$.baseFeeVaultWithdrawalNetwork");
l1FeeVaultRecipient = stdJson.readAddress(_json, "$.l1FeeVaultRecipient");
l1FeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.l1FeeVaultMinimumWithdrawalAmount");
l1FeeVaultWithdrawalNetwork = stdJson.readUint(_json, "$.l1FeeVaultWithdrawalNetwork");
sequencerFeeVaultRecipient = stdJson.readAddress(_json, "$.sequencerFeeVaultRecipient");
sequencerFeeVaultMinimumWithdrawalAmount = stdJson.readUint(_json, "$.sequencerFeeVaultMinimumWithdrawalAmount");
sequencerFeeVaultWithdrawalNetwork = stdJson.readUint(_json, "$.sequencerFeeVaultWithdrawalNetwork");
Expand Down
488 changes: 263 additions & 225 deletions packages/contracts-bedrock/scripts/L2Genesis.s.sol

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/contracts-bedrock/src/libraries/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ library Constants {
return config;
}
}

179 changes: 179 additions & 0 deletions packages/contracts-bedrock/test/L2Genesis.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

import { Test } from "forge-std/Test.sol";
import { L2Genesis } from "scripts/L2Genesis.s.sol";
import { VmSafe } from "forge-std/Vm.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { console } from "forge-std/console.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { LibString } from "solady/utils/LibString.sol";
import { Constants } from "src/libraries/Constants.sol";

contract L2GenesisTest is Test {
L2Genesis genesis;

function setUp() public {
vm.setEnv("CONTRACT_ADDRESSES_PATH", string.concat(vm.projectRoot(), "/test/mocks/addresses.json"));

genesis = new L2Genesis();
genesis.setUp();
}

function tmpfile() internal returns (string memory) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = "mktemp";
bytes memory result = vm.ffi(commands);
return string(result);
}

function deleteFile(string memory path) internal {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("rm ", path);
vm.ffi(commands);
}

function readJSON(string memory path) internal returns (string memory) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq < ", path);
return string(vm.ffi(commands));
}

function getJSONKeyCount(string memory path) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq 'keys | length' < ", path, " | xargs cast abi-encode 'f(uint256)'");
return abi.decode(vm.ffi(commands), (uint256));
}

// can this become a modifier?
function withTempDump(function (string memory) internal f) internal {
string memory path = tmpfile();
vm.setEnv("STATE_DUMP_PATH", path);
f(path);
deleteFile(path);
}

function getAccount(string memory path, string memory key) internal returns (string memory) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq '.[\"", key, "\"]' < ", path);
return string(vm.ffi(commands));
}

// this is slower..
function getBalance(string memory account) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("echo '", account, "' | jq -r '.balance'");
return vm.parseUint(string(vm.ffi(commands)));
}

function getAccountCountWithNoCodeAndNoBalance(string memory path) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq 'map_values(select(.nonce == \"0x0\" and .balance == \"0x0\")) | length' < ", path, " | xargs cast abi-encode 'f(uint256)'");
return abi.decode(vm.ffi(commands), (uint256));
}

// Go from keys
function getCode(string memory account) internal returns (bytes memory) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("echo '", account, "' | jq -r '.code'");
return bytes(vm.ffi(commands));
}

/// @notice Returns the number of accounts that contain particular code at a given path to a genesis file.
function getCodeCount(string memory path, string memory name) internal returns (uint256) {
bytes memory code = vm.getDeployedCode(name);
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq -r 'map_values(select(.code == \"", vm.toString(code), "\")) | length' < ", path, " | xargs cast abi-encode 'f(uint256)'");
return abi.decode(vm.ffi(commands), (uint256));
}

function getPredeployCountWithStorage(string memory path, uint256 count) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq 'map_values(.storage | select(length == ", vm.toString(count), ")) | keys | length' < ", path, " | xargs cast abi-encode 'f(uint256)'");
return abi.decode(vm.ffi(commands), (uint256));
}

function getPredeployCountWithSlotSet(string memory path, bytes32 slot) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq 'map_values(.storage | select(has(\"", vm.toString(slot), "\"))) | keys | length' < ", path, " | xargs cast abi-encode 'f(uint256)'");
return abi.decode(vm.ffi(commands), (uint256));
}

function getPredeployCountWithSlotSetToValue(string memory path, bytes32 slot, bytes32 value) internal returns (uint256) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
// jq 'map_values(.storage | select(."0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103" == "0x0000000000000000000000004200000000000000000000000000000000000018"))'
commands[2] = string.concat("jq 'map_values(.storage | select(.\"", vm.toString(slot), "\" == \"", vm.toString(value), "\")) | length' < ", path, " | xargs cast abi-encode 'f(uint256)'");
return abi.decode(vm.ffi(commands), (uint256));
}

function getImplementationAtAPath(string memory path, address addr) internal returns (address) {
string[] memory commands = new string[](3);
commands[0] = "bash";
commands[1] = "-c";
commands[2] = string.concat("jq -r '.\"", vm.toString(addr), "\".storage.\"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc\"' < ", path);
return address(uint160(uint256(abi.decode(vm.ffi(commands), (bytes32)))));
}

function testPredeployProxies() external {
withTempDump(_testPredeployProxies);
}

// TODO: there are 2 addresses that dont work
function _testPredeployProxies(string memory path) internal {
// Set the predeploy proxies into state
genesis.setPredeployProxies();
genesis.writeStateDump();

// 2 predeploys do not have proxies
assertEq(getCodeCount(path, "Proxy.sol:Proxy"), genesis.PREDEPLOY_COUNT() - 2);

// 17 proxies have the implementation set
assertEq(getPredeployCountWithSlotSet(path, Constants.PROXY_IMPLEMENTATION_ADDRESS), 17);

// All proxies except 2 have the proxy 1967 admin slot set to the proxy admin
assertEq(getPredeployCountWithSlotSetToValue(path, Constants.PROXY_OWNER_ADDRESS, bytes32(uint256(uint160(Predeploys.PROXY_ADMIN)))), genesis.PREDEPLOY_COUNT() - 2);

// For each predeploy
assertEq(getImplementationAtAPath(path, Predeploys.L2_TO_L1_MESSAGE_PASSER), 0xC0D3C0d3C0d3c0d3C0d3C0D3c0D3c0d3c0D30016);
assertEq(getImplementationAtAPath(path, Predeploys.L2_CROSS_DOMAIN_MESSENGER), 0xC0d3c0d3c0D3c0D3C0d3C0D3C0D3c0d3c0d30007);
assertEq(getImplementationAtAPath(path, Predeploys.L2_STANDARD_BRIDGE), 0xC0d3c0d3c0D3c0d3C0D3c0D3C0d3C0D3C0D30010);
assertEq(getImplementationAtAPath(path, Predeploys.L2_ERC721_BRIDGE), 0xC0D3c0d3c0d3c0d3c0D3C0d3C0D3C0D3c0d30014);
assertEq(getImplementationAtAPath(path, Predeploys.SEQUENCER_FEE_WALLET), 0xC0D3C0d3c0d3c0d3C0D3c0d3C0D3c0d3c0D30011);
assertEq(getImplementationAtAPath(path, Predeploys.OPTIMISM_MINTABLE_ERC20_FACTORY), 0xc0D3c0d3C0d3c0d3c0D3c0d3c0D3c0D3c0D30012);
assertEq(getImplementationAtAPath(path, Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY), 0xc0d3C0d3C0d3C0d3C0d3c0d3C0D3C0d3C0D30017);
assertEq(getImplementationAtAPath(path, Predeploys.L1_BLOCK_ATTRIBUTES), 0xc0d3C0D3C0D3c0D3C0D3C0d3C0D3c0D3c0d30015);
//assertEq(getImplementationAtAPath(path, Predeploys.GAS_PRICE_ORACLE), 0xc0d3C0d3C0d3c0D3C0D3C0d3C0d3C0D3C0D3000f);
assertEq(getImplementationAtAPath(path, Predeploys.DEPLOYER_WHITELIST), 0xc0d3c0d3C0d3c0D3c0d3C0D3c0d3C0d3c0D30002);
assertEq(getImplementationAtAPath(path, Predeploys.L1_BLOCK_NUMBER), 0xC0D3C0d3C0D3c0D3C0d3c0D3C0d3c0d3C0d30013);
assertEq(getImplementationAtAPath(path, Predeploys.LEGACY_MESSAGE_PASSER), 0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000);
assertEq(getImplementationAtAPath(path, Predeploys.PROXY_ADMIN), 0xC0d3C0D3c0d3C0d3c0d3c0D3C0D3C0d3C0D30018);
assertEq(getImplementationAtAPath(path, Predeploys.BASE_FEE_VAULT), 0xC0d3c0D3c0d3C0D3C0D3C0d3c0D3C0D3c0d30019);
//assertEq(getImplementationAtAPath(path, Predeploys.L1_FEE_VAULT), 0xc0D3c0D3C0d3c0d3c0d3C0d3c0d3C0d3C0D3001A);
assertEq(getImplementationAtAPath(path, Predeploys.SCHEMA_REGISTRY), 0xc0d3c0d3c0d3C0d3c0d3C0D3C0D3c0d3C0D30020);
assertEq(getImplementationAtAPath(path, Predeploys.EAS), 0xC0D3c0D3C0d3c0D3c0D3C0D3c0D3c0d3c0d30021);
}
}
16 changes: 12 additions & 4 deletions packages/contracts-bedrock/test/mocks/EIP1967Helper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,28 @@ import { Vm } from "forge-std/Vm.sol";
library EIP1967Helper {
/// @notice The storage slot that holds the address of a proxy implementation.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)`
bytes32 internal constant PROXY_IMPLEMENTATION_ADDRESS =
bytes32 internal constant PROXY_IMPLEMENTATION_SLOT =
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

/// @notice The storage slot that holds the address of the owner.
/// @dev `bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1)`
bytes32 internal constant PROXY_OWNER_ADDRESS = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
bytes32 internal constant PROXY_ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

Vm internal constant vm = Vm(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);

function getAdmin(address _proxy) internal view returns (address) {
return address(uint160(uint256(vm.load(address(_proxy), PROXY_OWNER_ADDRESS))));
return address(uint160(uint256(vm.load(address(_proxy), PROXY_ADMIN_SLOT))));
}

function setAdmin(address _addr, address _admin) internal {
vm.store(_addr, PROXY_ADMIN_SLOT, bytes32(uint256(uint160(_admin))));
}

function getImplementation(address _proxy) internal view returns (address) {
return address(uint160(uint256(vm.load(address(_proxy), PROXY_IMPLEMENTATION_ADDRESS))));
return address(uint160(uint256(vm.load(address(_proxy), PROXY_IMPLEMENTATION_SLOT))));
}

function setImplementation(address _addr, address _admin) internal {
vm.store(_addr, PROXY_IMPLEMENTATION_SLOT, bytes32(uint256(uint160(_admin))));
}
}
30 changes: 30 additions & 0 deletions packages/contracts-bedrock/test/mocks/addresses.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"AddressManager": "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9",
"DisputeGameFactory": "0x443d8fa004E7Ab97594A93ecC0b84b27dC595FED",
"DisputeGameFactoryProxy": "0x756e0562323ADcDA4430d6cb456d9151f605290B",
"L1CrossDomainMessenger": "0x71fA82Ea96672797954C28032b337aA40AAFC99f",
"L1CrossDomainMessengerProxy": "0x13aa49bAc059d709dd0a18D6bb63290076a702D7",
"L1ERC721Bridge": "0x44637A4292E0CD2B17A55d5F6B2F05AFcAcD0586",
"L1ERC721BridgeProxy": "0x3381cD18e2Fb4dB236BF0525938AB6E43Db0440f",
"L1StandardBridge": "0x0Da314776B267D898dEE57F6Ede357ae28b3b83c",
"L1StandardBridgeProxy": "0x96d3F6c20EEd2697647F543fE6C08bC2Fbf39758",
"L2OutputOracle": "0x19652082F846171168Daf378C4fD3ee85a0D4A60",
"L2OutputOracleProxy": "0x1aF7f588A501EA2B5bB3feeFA744892aA2CF00e6",
"Mips": "0xc9dCf0B40e4229049402025091A809a967840FA2",
"OptimismMintableERC20Factory": "0x39Aea2Dd53f2d01c15877aCc2791af6BDD7aD567",
"OptimismMintableERC20FactoryProxy": "0xDB25A7b768311dE128BBDa7B8426c3f9C74f3240",
"OptimismPortal": "0x8887E7568E81405c4E0D4cAaabdda949e3B9d4E4",
"OptimismPortal2": "0x9551B6e27B135D3929Feaa8088c92E8b5d6dFbf0",
"OptimismPortalProxy": "0x3D7Ebc40AF7092E3F1C81F2e996cbA5Cae2090d7",
"PreimageOracle": "0x4807Ac1C24f0f415466E892C1de4FAB06b0390Ac",
"ProtocolVersions": "0xfbfD64a6C0257F613feFCe050Aa30ecC3E3d7C3F",
"ProtocolVersionsProxy": "0x15cF58144EF33af1e14b5208015d11F9143E27b9",
"ProxyAdmin": "0xc7183455a4C133Ae270771860664b6B7ec320bB1",
"SafeProxyFactory": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f",
"SafeSingleton": "0x2e234DAe75C793f67A35089C9d99245E1C58470b",
"SuperchainConfig": "0x068E44eB31e111028c41598E4535be7468674D0A",
"SuperchainConfigProxy": "0xA4AD4f68d0b91CFD19687c881e50f3A00242828c",
"SystemConfig": "0xffbA8944650e26653823658d76A122946F27e2f2",
"SystemConfigProxy": "0xD16d567549A2a2a2005aEACf7fB193851603dd70",
"SystemOwnerSafe": "0xEC0Ed5dAF1E398eC507e7041Ca8702D85D770bb8"
}