diff --git a/.envrc b/.envrc new file mode 100644 index 0000000000..d583903354 --- /dev/null +++ b/.envrc @@ -0,0 +1,34 @@ +use flake + +# Admin account +export GS_ADMIN_ADDRESS=0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633 +export GS_ADMIN_PRIVATE_KEY=0x9813ae9317088a52c5a339bb68840ebbf1dcfe9a8286146b95c81ed66fbd177a + +# Batcher account +export GS_BATCHER_ADDRESS=0x30Cc02A155051F7709585c8734369bF7ff8697E1 +export GS_BATCHER_PRIVATE_KEY=0x4458a596f1b03081ae02f0be4d89aff4ee4f5a0d0b1f30a225547040aabe0134 + +# Proposer account +export GS_PROPOSER_ADDRESS=0x31Cb20A9eFEa1e857f7D53510eaf0012B4A67FC9 +export GS_PROPOSER_PRIVATE_KEY=0x85d9c71976a3f0ecf00e320909e9b0e392c2b68388d36721ce5a2978253b1988 + +# Sequencer account +export GS_SEQUENCER_ADDRESS=0xc30Bd3Dd52Ba3804110bE713856aE6ff97eB4d80 +export GS_SEQUENCER_PRIVATE_KEY=0x1e536548463714a79154ab682bafc73382d4021768f645d850baf586c4aad989 + +export L1_RPC_URL="https://eth-holesky.g.alchemy.com/v2/DvJycB1yIoYFD34g6ezRt_n4rG1hAyn2" +export L1_BLOCK_TIME=12 +export L1_CHAIN_ID=17000 + +export L2_CHAIN_ID=42069 +export L2_BLOCK_TIME=2 + +export ADDRESS=0x35Ec8a72D8e218C252EaE18044b0cBb97c1e57bF +export PRIVATE_KEY=0xe6fd96073c41d6a2a6d3e03ca7a59ed5846c6b1c3bec47d53f8a7d4770a16ff0 + +export DEPLOYMENT_OUTFILE=deployments/artifact.json +export DEPLOY_CONFIG_PATH=deploy-config/holesky.json + +export FOUNDRY_DISABLE_NIGHTLY_WARNING=true + +export IMPL_SALT=$(openssl rand -hex 32) diff --git a/.gitignore b/.gitignore index 5fc198d025..d40b29d99c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ packages/contracts-bedrock/deployments/anvil .secrets .env -.env* !.env.example !.envrc.example *.log @@ -46,3 +45,5 @@ __pycache__ # Ignore echidna artifacts crytic-export + +.direnv diff --git a/docker-bake.hcl b/docker-bake.hcl index 5f7f311cef..b53c7add0f 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -214,6 +214,17 @@ target "proofs-tools" { tags = [for tag in split(",", IMAGE_TAGS) : "${REGISTRY}/${REPOSITORY}/proofs-tools:${tag}"] } +target "holocene-deployer" { + dockerfile = "./packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile" + context = "./packages/contracts-bedrock/scripts/upgrades/holocene" + args = { + REV = "op-contracts/v1.8.0-rc.1" + } + target="holocene-deployer" + platforms = split(",", PLATFORMS) + tags = [for tag in split(",", IMAGE_TAGS) : "${REGISTRY}/${REPOSITORY}/holocene-deployer:${tag}"] +} + target "ci-builder" { dockerfile = "./ops/docker/ci-builder/Dockerfile" context = "." diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000000..f8ef1f25fc --- /dev/null +++ b/flake.lock @@ -0,0 +1,148 @@ +{ + "nodes": { + "devshell": { + "inputs": { + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1735644329, + "narHash": "sha256-tO3HrHriyLvipc4xr+Ewtdlo7wM1OjXNjlWRgmM7peY=", + "owner": "numtide", + "repo": "devshell", + "rev": "f7795ede5b02664b57035b3b757876703e2c3eac", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "devshell", + "type": "github" + } + }, + "flake-utils": { + "locked": { + "lastModified": 1644229661, + "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "foundry": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1739524197, + "narHash": "sha256-RW8acIrHvzWNplAfOW6/axc3d7AzdJddS/cjUFbldWI=", + "owner": "shazow", + "repo": "foundry.nix", + "rev": "6df02c17070c6c4587f2126a01604b634024ada9", + "type": "github" + }, + "original": { + "owner": "shazow", + "repo": "foundry.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1722073938, + "narHash": "sha256-OpX0StkL8vpXyWOGUD6G+MA26wAXK6SpT94kLJXo6B4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e36e9f57337d0ff0cf77aceb58af4c805472bfae", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1666753130, + "narHash": "sha256-Wff1dGPFSneXJLI2c0kkdWTgxnQ416KE6X4KnFkgPYQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f540aeda6f677354f1e7144ab04352f61aaa0118", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1737469691, + "narHash": "sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devshell": "devshell", + "foundry": "foundry", + "nixpkgs": "nixpkgs_3", + "systems": "systems", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1737483750, + "narHash": "sha256-5An1wq5U8sNycOBBg3nsDDgpwBmR9liOpDGlhliA6Xo=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "f2cc121df15418d028a59c9737d38e3a90fbaf8f", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000000..da73e9f4de --- /dev/null +++ b/flake.nix @@ -0,0 +1,58 @@ +{ + description = ""; + inputs = { + devshell = { + url = "github:numtide/devshell"; + inputs.systems.follows = "systems"; + }; + nixpkgs = { + url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + systems.url = "github:nix-systems/default"; + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + foundry.url = "github:shazow/foundry.nix"; + }; + + outputs = + { + self, + nixpkgs, + systems, + foundry, + ... + }@inputs: + let + eachSystem = + f: + nixpkgs.lib.genAttrs (import systems) ( + system: + f ( + import nixpkgs { + inherit system; + config = { + allowUnfree = true; + }; + overlays = [ foundry.overlay ]; + } + ) + ); + in + { + devShells = eachSystem (pkgs: { + default = pkgs.mkShell { + buildInputs = [ pkgs.foundry-bin ]; + packages = [ + pkgs.go + pkgs.goda + pkgs.gopls + pkgs.gops + pkgs.goreman + pkgs.just + ]; + }; + }); + }; +} diff --git a/ops/scripts/ci-docker-tag-op-stack-release.sh b/ops/scripts/ci-docker-tag-op-stack-release.sh index 45dd920949..1de86b749d 100755 --- a/ops/scripts/ci-docker-tag-op-stack-release.sh +++ b/ops/scripts/ci-docker-tag-op-stack-release.sh @@ -6,7 +6,7 @@ DOCKER_REPO=$1 GIT_TAG=$2 GIT_SHA=$3 -IMAGE_NAME=$(echo "$GIT_TAG" | grep -Eow '^(ci-builder(-rust)?|da-server|proofs-tools|cannon|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)' || true) +IMAGE_NAME=$(echo "$GIT_TAG" | grep -Eow '^(ci-builder(-rust)?|da-server|proofs-tools|holocene-deployer|cannon|ufm-[a-z0-9\-]*|op-[a-z0-9\-]*)' || true) if [ -z "$IMAGE_NAME" ]; then echo "image name could not be parsed from git tag '$GIT_TAG'" exit 1 diff --git a/packages/contracts-bedrock/deploy-config/holesky.json b/packages/contracts-bedrock/deploy-config/holesky.json new file mode 100644 index 0000000000..bba127c078 --- /dev/null +++ b/packages/contracts-bedrock/deploy-config/holesky.json @@ -0,0 +1,79 @@ +{ + + "l1StartingBlockTag": "0xf599a193ef3b9e7cca7211828c2ee6bcb6a7b683e4dd95a2b40876033381f05e", + + "l1ChainID": 17000, + "l2ChainID": 42069, + "l2BlockTime": 2, + "l1BlockTime": 12, + + "maxSequencerDrift": 600, + "sequencerWindowSize": 3600, + "channelTimeout": 300, + + "p2pSequencerAddress": "0xc30Bd3Dd52Ba3804110bE713856aE6ff97eB4d80", + "batchInboxAddress": "0xff00000000000000000000000000000000042069", + "batchSenderAddress": "0x30Cc02A155051F7709585c8734369bF7ff8697E1", + + "l2OutputOracleSubmissionInterval": 120, + "l2OutputOracleStartingBlockNumber": 0, + "l2OutputOracleStartingTimestamp": 1739539128, + + "l2OutputOracleProposer": "0x31Cb20A9eFEa1e857f7D53510eaf0012B4A67FC9", + "l2OutputOracleChallenger": "0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633", + + "finalizationPeriodSeconds": 12, + + "proxyAdminOwner": "0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633", + "baseFeeVaultRecipient": "0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633", + "l1FeeVaultRecipient": "0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633", + "sequencerFeeVaultRecipient": "0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633", + "finalSystemOwner": "0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633", + "superchainConfigGuardian": "0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633", + + "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", + "baseFeeVaultWithdrawalNetwork": 0, + "l1FeeVaultWithdrawalNetwork": 0, + "sequencerFeeVaultWithdrawalNetwork": 0, + + "gasPriceOracleOverhead": 0, + "gasPriceOracleScalar": 1000000, + + "enableGovernance": true, + "governanceTokenSymbol": "OP", + "governanceTokenName": "Optimism", + "governanceTokenOwner": "0xA487AACa83dc0261Ec6ecC049DEc1a114Be09633", + + "l2GenesisBlockGasLimit": "0x1c9c380", + "l2GenesisBlockBaseFeePerGas": "0x3b9aca00", + + "eip1559Denominator": 50, + "eip1559DenominatorCanyon": 250, + "eip1559Elasticity": 6, + "l2GenesisFjordTimeOffset": "0x0", + "l2GenesisRegolithTimeOffset": "0x0", + "l2GenesisEcotoneTimeOffset": "0x0", + "l2GenesisDeltaTimeOffset": "0x0", + "l2GenesisCanyonTimeOffset": "0x0", + "systemConfigStartBlock": 0, + + "requiredProtocolVersion": "0x0000000000000000000000000000000000000009000000000000000000000000", + "recommendedProtocolVersion": "0x0000000000000000000000000000000000000009000000000000000000000000", + + "faultGameAbsolutePrestate": "0x03c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98", + "faultGameMaxDepth": 44, + "faultGameClockExtension": 0, + "faultGameMaxClockDuration": 1200, + "faultGameGenesisBlock": 0, + "faultGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", + "faultGameSplitDepth": 14, + "faultGameWithdrawalDelay": 600, + + "preimageOracleMinProposalSize": 1800000, + "preimageOracleChallengePeriod": 300, + + "proofMaturityDelaySeconds": 604800, + "disputeGameFinalityDelaySeconds": 302400 +} diff --git a/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json b/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json index ff87fc4f8c..4239d10c3d 100644 --- a/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json +++ b/packages/contracts-bedrock/deploy-config/sepolia-devnet-0.json @@ -65,7 +65,7 @@ "eip1559Denominator": 250, "eip1559DenominatorCanyon": 250, "systemConfigStartBlock": 4071248, - "faultGameAbsolutePrestate": "0x0385c3f8ee78491001d92b90b07d0cf387b7b52ab9b83b4d87c994e92cf823ba", + "faultGameAbsolutePrestate": "0x03925193e3e89f87835bbdf3a813f60b2aa818a36bbe71cd5d8fd7e79f5e8afe", "faultGameMaxDepth": 73, "faultGameClockExtension": 3600, "faultGameMaxClockDuration": 14400, diff --git a/packages/contracts-bedrock/deployments/artifact.json b/packages/contracts-bedrock/deployments/artifact.json new file mode 100644 index 0000000000..cebf44342c --- /dev/null +++ b/packages/contracts-bedrock/deployments/artifact.json @@ -0,0 +1,21 @@ +{ + "DelayedWETH": "0xA45784AdF095e44548291e44015691544F83Ed40", + "DisputeGameFactory": "0x1C4d36E34DdC0B7fB402Be75D779659BD5DD2130", + "L1CrossDomainMessenger": "0xf3F7c4F5f5d3C2E681F541d5258706788AB15Ca9", + "L1ERC721Bridge": "0x084c7A6951Da96331ac36e5FF34E106bED8A9877", + "L1StandardBridge": "0xE762e8A993377f0C3EEd2DE8558EC423b8d9E06c", + "L2OutputOracle": "0x299234a263A8fc8e2f1f0663436C1F96e050808E", + "Mips": "0x778A687a289dF0d07D80C12AA66D89Ad3440B3e9", + "OPContractsManager": "0x9E035C59EDB7Ae2b18E7743ddF8acc354ba44795", + "OPContractsManagerProxy": "0x897712Bf97AedC30efbb0d7745DEd6feE4176D3f", + "OptimismMintableERC20Factory": "0x8d428bdF2EE9a258faE8f0C97A48c74f774a556C", + "OptimismPortal": "0x2b06a099EbEc56B14a35fBb49dBB5e5987F413f8", + "OptimismPortal2": "0x461D688868755997f0127Ceba7D7411A75A8446A", + "PreimageOracle": "0xe0d9712F49C104889B2f5b5ac4132b404324f4c1", + "ProtocolVersions": "0x61d390288303E4cb6236A480CCe3e7a2a161b551", + "ProtocolVersionsProxy": "0xC9ad849Ea59981dd92F67F301153c1648629d0dd", + "SuperchainConfig": "0x03A7EB4EDc3E7A7867a48c340dBd356e2e8Da7d6", + "SuperchainConfigProxy": "0x87F67eba4958038cdDd08144A739882e05f478B7", + "SuperchainProxyAdmin": "0xa998ee6e2a795bB2126F6fFddcc3aee4233efF9C", + "SystemConfig": "0x7BC7c31B78Aa02ce8019C9DDEe6374fFC8fab5aE" +} \ No newline at end of file diff --git a/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh b/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh index 81e7c6476d..2aeffc7d35 100755 --- a/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh +++ b/packages/contracts-bedrock/scripts/checks/check-semver-diff.sh @@ -22,7 +22,7 @@ if ! { git diff origin/develop...HEAD --name-only; git diff --name-only; git dif fi # Get the upstream semver-lock.json. -git show origin/develop:packages/contracts-bedrock/semver-lock.json > "$temp_dir/upstream_semver_lock.json" +git show origin/develop:packages/contracts-bedrock/snapshots/semver-lock.json > "$temp_dir/upstream_semver_lock.json" # Copy the local semver-lock.json. cp "$SEMVER_LOCK" "$temp_dir/local_semver_lock.json" diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index d40c03987d..73fe4cc83d 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -279,43 +279,43 @@ contract Deploy is Deployer { deployImplementations({ _isInterop: cfg.useInterop() }); - // Deploy Current OPChain Contracts - deployOpChain(); - - // Apply modifications for non-standard configurations not supported by the OPCM deployment - if (cfg.useFaultProofs()) { - vm.startPrank(ISuperchainConfig(mustGetAddress("SuperchainConfigProxy")).guardian()); - IOptimismPortal2(mustGetAddress("OptimismPortalProxy")).setRespectedGameType( - GameType.wrap(uint32(cfg.respectedGameType())) - ); - vm.stopPrank(); - } else { - // The L2OutputOracle is not deployed by the OPCM, we deploy the proxy and initialize it here. - deployERC1967Proxy("L2OutputOracleProxy"); - initializeL2OutputOracle(); - - // The OptimismPortalProxy contract is used both with and without Fault Proofs enabled, and is deployed by - // deployOPChain. If Fault Proofs are disabled, then we need to reinitialize the OptimismPortalProxy - // as the legacy OptimismPortal. - resetInitializedProxy("OptimismPortal"); - initializeOptimismPortal(); - } - - if (cfg.useCustomGasToken()) { - // Reset the systemconfig then reinitialize it with the custom gas token - resetInitializedProxy("SystemConfig"); - initializeSystemConfig(); - } - - if (cfg.useAltDA()) { - bytes32 typeHash = keccak256(bytes(cfg.daCommitmentType())); - bytes32 keccakHash = keccak256(bytes("KeccakCommitment")); - if (typeHash == keccakHash) { - setupOpAltDA(); - } - } - - transferProxyAdminOwnership(); + // // Deploy Current OPChain Contracts + // deployOpChain(); + + // // Apply modifications for non-standard configurations not supported by the OPCM deployment + // if (cfg.useFaultProofs()) { + // vm.startPrank(ISuperchainConfig(mustGetAddress("SuperchainConfigProxy")).guardian()); + // IOptimismPortal2(mustGetAddress("OptimismPortalProxy")).setRespectedGameType( + // GameType.wrap(uint32(cfg.respectedGameType())) + // ); + // vm.stopPrank(); + // } else { + // // The L2OutputOracle is not deployed by the OPCM, we deploy the proxy and initialize it here. + // deployERC1967Proxy("L2OutputOracleProxy"); + // initializeL2OutputOracle(); + + // // The OptimismPortalProxy contract is used both with and without Fault Proofs enabled, and is deployed by + // // deployOPChain. If Fault Proofs are disabled, then we need to reinitialize the OptimismPortalProxy + // // as the legacy OptimismPortal. + // resetInitializedProxy("OptimismPortal"); + // initializeOptimismPortal(); + // } + + // if (cfg.useCustomGasToken()) { + // // Reset the systemconfig then reinitialize it with the custom gas token + // resetInitializedProxy("SystemConfig"); + // initializeSystemConfig(); + // } + + // if (cfg.useAltDA()) { + // bytes32 typeHash = keccak256(bytes(cfg.daCommitmentType())); + // bytes32 keccakHash = keccak256(bytes("KeccakCommitment")); + // if (typeHash == keccakHash) { + // setupOpAltDA(); + // } + // } + + // transferProxyAdminOwnership(); console.log("set up op chain!"); } @@ -329,6 +329,7 @@ contract Deploy is Deployer { /// 2. The ProtocolVersions contract function deploySuperchain() public { console.log("Setting up Superchain"); + console.log("deployer: ", msg.sender); DeploySuperchain ds = new DeploySuperchain(); (DeploySuperchainInput dsi, DeploySuperchainOutput dso) = ds.etchIOContracts(); @@ -342,7 +343,8 @@ contract Deploy is Deployer { dsi.set(dsi.recommendedProtocolVersion.selector, ProtocolVersion.wrap(cfg.recommendedProtocolVersion())); // Run the deployment script. - ds.run(dsi, dso); + ds.run(msg.sender, dsi, dso); + save("SuperchainProxyAdmin", address(dso.superchainProxyAdmin())); save("SuperchainConfigProxy", address(dso.superchainConfigProxy())); save("SuperchainConfig", address(dso.superchainConfigImpl())); @@ -371,6 +373,8 @@ contract Deploy is Deployer { DeployImplementations di = new DeployImplementations(); (DeployImplementationsInput dii, DeployImplementationsOutput dio) = di.etchIOContracts(); + dii.set(dii.salt.selector, _implSalt()); + dii.set(dii.withdrawalDelaySeconds.selector, cfg.faultGameWithdrawalDelay()); dii.set(dii.minProposalSizeBytes.selector, cfg.preimageOracleMinProposalSize()); dii.set(dii.challengePeriodSeconds.selector, cfg.preimageOracleChallengePeriod()); @@ -389,7 +393,7 @@ contract Deploy is Deployer { if (_isInterop) { di = DeployImplementations(new DeployImplementationsInterop()); } - di.run(dii, dio); + di.run(msg.sender, dii, dio); // Temporary patch for legacy system if (!cfg.useFaultProofs()) { diff --git a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol index c9048a07df..4cdcf76de8 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployImplementations.s.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +import { console2 as console } from "forge-std/console2.sol"; + import { Script } from "forge-std/Script.sol"; import { LibString } from "@solady/utils/LibString.sol"; @@ -457,7 +459,11 @@ contract DeployImplementationsOutput is BaseDeployIO { contract DeployImplementations is Script { // -------- Core Deployment Methods -------- - function run(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public { + address caller; + + function run(address _caller, DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public { + caller = _caller; + // Deploy the implementations. deploySystemConfigImpl(_dii, _dio); deployL1CrossDomainMessengerImpl(_dii, _dio); @@ -555,11 +561,11 @@ contract DeployImplementations is Script { { address opcmProxyOwner = _dii.opcmProxyOwner(); - vm.broadcast(msg.sender); + vm.broadcast(caller); IProxy proxy = IProxy( DeployUtils.create1({ _name: "Proxy", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (caller))) }) ); @@ -569,7 +575,7 @@ contract DeployImplementations is Script { OPContractsManager.InitializerInputs memory initializerInputs = OPContractsManager.InitializerInputs(_blueprints, _setters, _release, true); - vm.startBroadcast(msg.sender); + vm.startBroadcast(caller); proxy.upgradeToAndCall(address(opcmImpl), abi.encodeCall(opcmImpl.initialize, (initializerInputs))); proxy.changeAdmin(address(opcmProxyOwner)); // transfer ownership of Proxy contract to the ProxyAdmin contract @@ -590,19 +596,9 @@ contract DeployImplementations is Script { // First we deploy the blueprints for the singletons deployed by OPCM. // forgefmt: disable-start bytes32 salt = _dii.salt(); - OPContractsManager.Blueprints memory blueprints; - - vm.startBroadcast(msg.sender); - blueprints.addressManager = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AddressManager")), salt); - blueprints.proxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("Proxy")), salt); - blueprints.proxyAdmin = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ProxyAdmin")), salt); - blueprints.l1ChugSplashProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("L1ChugSplashProxy")), salt); - blueprints.resolvedDelegateProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ResolvedDelegateProxy")), salt); - blueprints.anchorStateRegistry = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AnchorStateRegistry")), salt); - (blueprints.permissionedDisputeGame1, blueprints.permissionedDisputeGame2) = deployBigBytecode(vm.getCode("PermissionedDisputeGame"), salt); - vm.stopBroadcast(); - // forgefmt: disable-end + OPContractsManager.Blueprints memory blueprints = deployBlueprints(salt); + OPContractsManager.ImplementationSetter[] memory setters = new OPContractsManager.ImplementationSetter[](9); setters[0] = OPContractsManager.ImplementationSetter({ name: "L1ERC721Bridge", @@ -647,6 +643,31 @@ contract DeployImplementations is Script { _dio.set(_dio.opcmProxy.selector, address(opcmProxy)); } + function deployBlueprints(bytes32 salt) public returns (OPContractsManager.Blueprints memory){ + vm.startBroadcast(caller); + address addressManager = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AddressManager")), salt); + address proxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("Proxy")), salt); + address proxyAdmin = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ProxyAdmin")), salt); + address l1ChugSplashProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("L1ChugSplashProxy")), salt); + address resolvedDelegateProxy = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("ResolvedDelegateProxy")), salt); + address anchorStateRegistry = deployBytecode(Blueprint.blueprintDeployerBytecode(vm.getCode("AnchorStateRegistry")), salt); + (address permissionedDisputeGame1, address permissionedDisputeGame2) = deployBigBytecode(vm.getCode("PermissionedDisputeGame"), salt); + vm.stopBroadcast(); + // forgefmt: disable-end + + return OPContractsManager.Blueprints({ + addressManager: addressManager, + proxy: proxy, + proxyAdmin: proxyAdmin, + l1ChugSplashProxy: l1ChugSplashProxy, + resolvedDelegateProxy: resolvedDelegateProxy, + anchorStateRegistry: anchorStateRegistry, + permissionedDisputeGame1: permissionedDisputeGame1, + permissionedDisputeGame2: permissionedDisputeGame2 + }); + + } + // --- Core Contracts --- function deploySystemConfigImpl(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public virtual { @@ -661,7 +682,7 @@ contract DeployImplementations is Script { impl = ISystemConfig(existingImplementation); } else if (isDevelopRelease(release)) { // Deploy a new implementation for development builds. - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = ISystemConfig( DeployUtils.create1({ _name: "SystemConfig", @@ -692,7 +713,7 @@ contract DeployImplementations is Script { if (existingImplementation != address(0)) { impl = IL1CrossDomainMessenger(existingImplementation); } else if (isDevelopRelease(release)) { - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = IL1CrossDomainMessenger( DeployUtils.create1({ _name: "L1CrossDomainMessenger", @@ -723,7 +744,7 @@ contract DeployImplementations is Script { if (existingImplementation != address(0)) { impl = IL1ERC721Bridge(existingImplementation); } else if (isDevelopRelease(release)) { - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = IL1ERC721Bridge( DeployUtils.create1({ _name: "L1ERC721Bridge", @@ -754,7 +775,7 @@ contract DeployImplementations is Script { if (existingImplementation != address(0)) { impl = IL1StandardBridge(payable(existingImplementation)); } else if (isDevelopRelease(release)) { - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = IL1StandardBridge( DeployUtils.create1({ _name: "L1StandardBridge", @@ -785,7 +806,7 @@ contract DeployImplementations is Script { if (existingImplementation != address(0)) { impl = IOptimismMintableERC20Factory(existingImplementation); } else if (isDevelopRelease(release)) { - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = IOptimismMintableERC20Factory( DeployUtils.create1({ _name: "OptimismMintableERC20Factory", @@ -810,7 +831,7 @@ contract DeployImplementations is Script { ISuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); IProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); - vm.broadcast(msg.sender); + vm.broadcast(caller); // TODO: Eventually we will want to select the correct implementation based on the release. OPContractsManager impl = new OPContractsManager(superchainConfigProxy, protocolVersionsProxy); @@ -873,7 +894,7 @@ contract DeployImplementations is Script { } else if (isDevelopRelease(release)) { uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = IOptimismPortal2( DeployUtils.create1({ _name: "OptimismPortal2", @@ -903,7 +924,7 @@ contract DeployImplementations is Script { impl = IDelayedWETH(payable(existingImplementation)); } else if (isDevelopRelease(release)) { uint256 withdrawalDelaySeconds = _dii.withdrawalDelaySeconds(); - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = IDelayedWETH( DeployUtils.create1({ _name: "DelayedWETH", @@ -938,7 +959,7 @@ contract DeployImplementations is Script { } else if (isDevelopRelease(release)) { uint256 minProposalSizeBytes = _dii.minProposalSizeBytes(); uint256 challengePeriodSeconds = _dii.challengePeriodSeconds(); - vm.broadcast(msg.sender); + vm.broadcast(caller); singleton = IPreimageOracle( DeployUtils.create1({ _name: "PreimageOracle", @@ -967,7 +988,7 @@ contract DeployImplementations is Script { } else if (isDevelopRelease(release)) { uint256 mipsVersion = _dii.mipsVersion(); IPreimageOracle preimageOracle = IPreimageOracle(address(_dio.preimageOracleSingleton())); - vm.broadcast(msg.sender); + vm.broadcast(caller); singleton = IMIPS( DeployUtils.create1({ _name: mipsVersion == 1 ? "MIPS" : "MIPS2", @@ -998,7 +1019,7 @@ contract DeployImplementations is Script { if (existingImplementation != address(0)) { impl = IDisputeGameFactory(payable(existingImplementation)); } else if (isDevelopRelease(release)) { - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = IDisputeGameFactory( DeployUtils.create1({ _name: "DisputeGameFactory", @@ -1019,11 +1040,13 @@ contract DeployImplementations is Script { (dii_, dio_) = getIOContracts(); vm.etch(address(dii_), type(DeployImplementationsInput).runtimeCode); vm.etch(address(dio_), type(DeployImplementationsOutput).runtimeCode); + vm.allowCheatcodes(address(dii_)); + vm.allowCheatcodes(address(dio_)); } function getIOContracts() public view returns (DeployImplementationsInput dii_, DeployImplementationsOutput dio_) { - dii_ = DeployImplementationsInput(DeployUtils.toIOAddress(msg.sender, "optimism.DeployImplementationsInput")); - dio_ = DeployImplementationsOutput(DeployUtils.toIOAddress(msg.sender, "optimism.DeployImplementationsOutput")); + dii_ = DeployImplementationsInput(DeployUtils.toIOAddress(caller, "optimism.DeployImplementationsInput")); + dio_ = DeployImplementationsOutput(DeployUtils.toIOAddress(caller, "optimism.DeployImplementationsOutput")); } function deployBytecode(bytes memory _bytecode, bytes32 _salt) public returns (address newContract_) { @@ -1129,11 +1152,11 @@ contract DeployImplementationsInterop is DeployImplementations { { address opcmProxyOwner = _dii.opcmProxyOwner(); - vm.broadcast(msg.sender); + vm.broadcast(caller); IProxy proxy = IProxy( DeployUtils.create1({ _name: "Proxy", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (caller))) }) ); @@ -1143,7 +1166,7 @@ contract DeployImplementationsInterop is DeployImplementations { OPContractsManager.InitializerInputs memory initializerInputs = OPContractsManager.InitializerInputs(_blueprints, _setters, _release, true); - vm.startBroadcast(msg.sender); + vm.startBroadcast(caller); proxy.upgradeToAndCall(address(opcmImpl), abi.encodeCall(opcmImpl.initialize, (initializerInputs))); proxy.changeAdmin(opcmProxyOwner); // transfer ownership of Proxy contract to the ProxyAdmin contract @@ -1170,7 +1193,7 @@ contract DeployImplementationsInterop is DeployImplementations { } else if (isDevelopRelease(release)) { uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = IOptimismPortalInterop( DeployUtils.create1({ _name: "OptimismPortalInterop", @@ -1207,7 +1230,7 @@ contract DeployImplementationsInterop is DeployImplementations { if (existingImplementation != address(0)) { impl = ISystemConfigInterop(existingImplementation); } else if (isDevelopRelease(release)) { - vm.broadcast(msg.sender); + vm.broadcast(caller); impl = ISystemConfigInterop( DeployUtils.create1({ _name: "SystemConfigInterop", @@ -1232,7 +1255,7 @@ contract DeployImplementationsInterop is DeployImplementations { ISuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); IProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); - vm.broadcast(msg.sender); + vm.broadcast(caller); // TODO: Eventually we will want to select the correct implementation based on the release. OPContractsManager impl = new OPContractsManagerInterop(superchainConfigProxy, protocolVersionsProxy); diff --git a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol index 74492556e1..1cc3bd7cf0 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeploySuperchain.s.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +import { console2 as console } from "forge-std/console2.sol"; + import { Script } from "forge-std/Script.sol"; import { stdToml } from "forge-std/StdToml.sol"; @@ -288,8 +290,10 @@ contract DeploySuperchainOutput is BaseDeployIO { // since they are set to the initial proxy admin owner. contract DeploySuperchain is Script { // -------- Core Deployment Methods -------- - - function run(DeploySuperchainInput _dsi, DeploySuperchainOutput _dso) public { + address caller; + + function run(address _caller, DeploySuperchainInput _dsi, DeploySuperchainOutput _dso) public { + caller = _caller; // Notice that we do not do any explicit verification here that inputs are set. This is because // the verification happens elsewhere: // - Getter methods on the input contract provide sanity checks that values are set, when applicable. @@ -321,11 +325,12 @@ contract DeploySuperchain is Script { // We explicitly specify the deployer as `msg.sender` because for testing we deploy this script from a test // contract. If we provide no argument, the foundry default sender would be the broadcaster during test, but the // broadcaster needs to be the deployer since they are set to the initial proxy admin owner. - vm.broadcast(msg.sender); + + vm.broadcast(caller); IProxyAdmin superchainProxyAdmin = IProxyAdmin( DeployUtils.create1({ _name: "ProxyAdmin", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxyAdmin.__constructor__, (msg.sender))) + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxyAdmin.__constructor__, (caller))) }) ); @@ -335,7 +340,7 @@ contract DeploySuperchain is Script { function deploySuperchainImplementationContracts(DeploySuperchainInput, DeploySuperchainOutput _dso) public { // Deploy implementation contracts. - vm.startBroadcast(msg.sender); + vm.startBroadcast(caller); ISuperchainConfig superchainConfigImpl = ISuperchainConfig( DeployUtils.create1({ _name: "SuperchainConfig", @@ -364,7 +369,7 @@ contract DeploySuperchain is Script { IProxyAdmin superchainProxyAdmin = _dso.superchainProxyAdmin(); ISuperchainConfig superchainConfigImpl = _dso.superchainConfigImpl(); - vm.startBroadcast(msg.sender); + vm.startBroadcast(caller); ISuperchainConfig superchainConfigProxy = ISuperchainConfig( DeployUtils.create1({ _name: "Proxy", @@ -380,6 +385,9 @@ contract DeploySuperchain is Script { ); vm.stopBroadcast(); + console.log("Deployed SuperchainConfigProxy", address(superchainConfigProxy)); + console.log("SuperchainConfigProxy.guardian", superchainConfigProxy.guardian()); + vm.label(address(superchainConfigProxy), "SuperchainConfigProxy"); _dso.set(_dso.superchainConfigProxy.selector, address(superchainConfigProxy)); } @@ -392,7 +400,7 @@ contract DeploySuperchain is Script { IProxyAdmin superchainProxyAdmin = _dso.superchainProxyAdmin(); IProtocolVersions protocolVersionsImpl = _dso.protocolVersionsImpl(); - vm.startBroadcast(msg.sender); + vm.startBroadcast(caller); IProtocolVersions protocolVersionsProxy = IProtocolVersions( DeployUtils.create1({ _name: "Proxy", @@ -421,7 +429,7 @@ contract DeploySuperchain is Script { IProxyAdmin superchainProxyAdmin = _dso.superchainProxyAdmin(); DeployUtils.assertValidContractAddress(address(superchainProxyAdmin)); - vm.broadcast(msg.sender); + vm.broadcast(caller); superchainProxyAdmin.transferOwnership(superchainProxyAdminOwner); } @@ -439,7 +447,7 @@ contract DeploySuperchain is Script { // This returns the addresses of the IO contracts for this script. function getIOContracts() public view returns (DeploySuperchainInput dsi_, DeploySuperchainOutput dso_) { - dsi_ = DeploySuperchainInput(DeployUtils.toIOAddress(msg.sender, "optimism.DeploySuperchainInput")); - dso_ = DeploySuperchainOutput(DeployUtils.toIOAddress(msg.sender, "optimism.DeploySuperchainOutput")); + dsi_ = DeploySuperchainInput(DeployUtils.toIOAddress(caller, "optimism.DeploySuperchainInput")); + dso_ = DeploySuperchainOutput(DeployUtils.toIOAddress(caller, "optimism.DeploySuperchainOutput")); } } diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example new file mode 100644 index 0000000000..037573528c --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example @@ -0,0 +1,101 @@ +############################################## +# ↓ Required ↓ # +############################################## + +# NOTE: The deploy config must be provided as a first argument to `just run`! + +# The network to deploy the contracts to. +# Must be one of 'mainnet', 'sepolia' +NETWORK= + +# Etherscan API key used to verify contract bytecode +ETHERSCAN_API_KEY= + +# RPC URL for the L1 network that matches $NETWORK +ETH_RPC_URL= + +# Private key used to deploy the new contracts for this upgrade +PRIVATE_KEY= + +# Address of deployed `PreimageOracle` contract. +PREIMAGE_ORACLE_ADDR= + +# Address of deployed `AnchorStateRegistry` proxy contract. +ANCHOR_STATE_REGISTRY_PROXY_ADDR= + +# Address of the `SuperchainConfig` proxy contract. +SUPERCHAIN_CONFIG_PROXY_ADDR= + +# Address of deployed `ProxyAdmin` contract. +PROXY_ADMIN_ADDR= + +# Address of deployed `SystemConfig` proxy contract. +SYSTEM_CONFIG_PROXY_ADDR= + +# Address of deployed `DisputeGameFactory` proxy contract. +DISPUTE_GAME_FACTORY_PROXY_ADDR= + +# Whether or not to deploy and include any fault proof contracts in the upgrade. +# +# If 'true', the `PermissionedDisputeGame` contract will be deployed and included in the upgrade. +# If 'false', the `PermissionedDisputeGame` contract will not be deployed or included in the upgrade. +# +# Must be one of 'true', 'false' +# Cannot be 'false' if `USE_PERMISSIONLESS_FAULT_PROOFS` is 'true' +USE_FAULT_PROOFS=true + +# Whether or not to deploy and include the `FaultDisputeGame` contract in the upgrade. +# +# If 'true', the `FaultDisputeGame` contract will be deployed and included in the upgrade. +# If 'false', the `FaultDisputeGame` contract will not be deployed or included in the upgrade. +# +# Must be one of 'true', 'false' +# Cannot be 'true' if `USE_FAULT_PROOFS` is 'false' +USE_PERMISSIONLESS_FAULT_PROOFS=true + +# op-contracts release to use for deployments and used for retrieving contract deployment addresses +# from the superchain-registry. +# +# An entry for this release has to exist in file +# https://github.com/ethereum-optimism/superchain-registry/blob/main/validation/standard/standard-versions-sepolia.toml +# for Sepolia or +# https://github.com/ethereum-optimism/superchain-registry/blob/main/validation/standard/standard-versions-mainnet.toml +# for Mainnet. +# +# Leave at default value unless you know what you're doing. +OP_CONTRACTS_RELEASE=v1.8.0-rc.4 + +################################################### +# ↓ Optional ↓ # +# Do not set if you don't know what you're doing. # +################################################### + +# Address of the deployed `SystemConfig` implementation for Holocene. +# +# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`. +# If you are not the first to deploy Holocene on this L1, this field should be set to +# the existing deployment address. +# +# If this field is not set, the `superchain-registry` will be consulted for the implementation address. +# If this field is set to the zero address, a new `SystemConfig` implementation will be deployed. +SYSTEM_CONFIG_IMPL_ADDR= + +# Address of the deployed `MIPS` implementation for Holocene. +# +# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`. +# If you are not the first to deploy Holocene on this L1, this field should be set to +# the existing deployment address. +# +# If this field is not set, the `superchain-registry` will be consulted for the implementation address. +# If this field is set to the zero address, a new `MIPS` implementation will be deployed. +MIPS_IMPL_ADDR= + +# Address of deployed `DelayedWETH` implementation contract. +# +# This implementation is reused across L2 deployments based on the L1 @ `ETH_RPC_URL`. +# If you are not the first to deploy permissionless fault proofs on L1, this field should be +# set to the existing deployment address. +# +# If this field is not set, the `superchain-registry` will be consulted for the implementation address. +# If this field is set to the zero address, a new `DelayedWETH` implementation will be deployed. +DELAYED_WETH_IMPL_ADDR= diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore b/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore new file mode 100644 index 0000000000..48835ab95e --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/.gitignore @@ -0,0 +1,3 @@ +# Environment +.env +output* diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol b/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol new file mode 100644 index 0000000000..8203e42b06 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/DeployUpgrade.s.sol @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +// Forge +import { console2 as console } from "forge-std/console2.sol"; + +// Scripts +import { Deployer } from "scripts/deploy/Deployer.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +// Utils +import { Claim, GameTypes, Duration } from "src/dispute/lib/Types.sol"; + +// Interfaces +import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; +import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; +import { IProxy } from "src/universal/interfaces/IProxy.sol"; +import { + IFaultDisputeGame, + IBigStepper, + IAnchorStateRegistry, + IDelayedWETH +} from "src/dispute/interfaces/IFaultDisputeGame.sol"; +import { IPermissionedDisputeGame } from "src/dispute/interfaces/IPermissionedDisputeGame.sol"; +import { IMIPS, IPreimageOracle } from "src/cannon/interfaces/IMIPS.sol"; + +/// @title DeployUpgrade +/// @notice A deployment script for smart contract upgrades surrounding the Holocene hardfork. +contract DeployUpgrade is Deployer { + /// @dev The entrypoint to the deployment script. + function deploy( + address _proxyAdmin, + address _superchainConfig, + address _systemConfigImpl, + address _mipsImpl, + address _delayedWETH, + address _preimageOracle, + address _anchorStateRegistry, + bool _useFaultProofs, + bool _usePermissionlessFaultProofs + ) + public + { + // Shim the existing contracts that this upgrade is dependent on. + shim({ + _proxyAdmin: _proxyAdmin, + _superchainConfig: _superchainConfig, + _systemConfigImpl: _systemConfigImpl, + _mipsImpl: _mipsImpl, + _delayedWETH: _delayedWETH, + _preimageOracle: _preimageOracle, + _anchorStateRegistry: _anchorStateRegistry + }); + + // Deploy conditional implementations. + if (_systemConfigImpl == address(0)) deploySystemConfigImplementation(); + + if (_useFaultProofs) { + if (_mipsImpl == address(0)) deployMIPSImplementation(); + if (_delayedWETH == address(0)) deployDelayedWETH(); + + // Deploy: + // 1. New `DelayedWETH` proxy contracts for the `FaultDisputeGame` and `PermissionedDisputeGame`. + // 2. New `FaultDisputeGame` and `PermissionedDisputeGame` implementation contracts. + deployDelayedWETHProxy("PDG"); + deployPermissionedDisputeGameImplementation(); + if (_usePermissionlessFaultProofs) { + deployDelayedWETHProxy("FDG"); + deployFaultDisputeGameImplementation(); + } + + // Run deployment checks. + checkMIPS(); + checkPermissionedDisputeGame(); + checkDelayedWETH("PDG"); + if (_usePermissionlessFaultProofs) { + checkFaultDisputeGame(); + checkDelayedWETH("FDG"); + } + } + + // Print the deployment summary. + printSummary(); + } + + /// @dev Shims the existing contracts that this upgrade is dependent on. + function shim( + address _proxyAdmin, + address _superchainConfig, + address _systemConfigImpl, + address _mipsImpl, + address _delayedWETH, + address _preimageOracle, + address _anchorStateRegistry + ) + public + { + prankDeployment("ProxyAdmin", _proxyAdmin); + prankDeployment("SuperchainConfig", _superchainConfig); + if (_systemConfigImpl != address(0)) prankDeployment("SystemConfig", _systemConfigImpl); + if (_mipsImpl != address(0)) prankDeployment("MIPS", _mipsImpl); + if (_delayedWETH != address(0)) prankDeployment("DelayedWETH", _delayedWETH); + prankDeployment("PreimageOracle", _preimageOracle); + prankDeployment("AnchorStateRegistry", _anchorStateRegistry); + } + + /// @dev Deploys the Holocene `SystemConfig` implementation contract. + function deploySystemConfigImplementation() public { + vm.broadcast(msg.sender); + address systemConfig = DeployUtils.create1( + "SystemConfig", DeployUtils.encodeConstructor(abi.encodeCall(ISystemConfig.__constructor__, ())) + ); + save("SystemConfig", systemConfig); + } + + /// @dev Deploys the new `MIPS` implementation contract. + function deployMIPSImplementation() public { + vm.broadcast(msg.sender); + address mips = DeployUtils.create1({ + _name: "MIPS", + _args: DeployUtils.encodeConstructor( + abi.encodeCall(IMIPS.__constructor__, (IPreimageOracle(mustGetAddress("PreimageOracle")))) + ) + }); + save("MIPS", mips); + } + + /// @dev Checks if the `MIPS` contract is correctly configured. + function checkMIPS() public view { + IMIPS mips = IMIPS(mustGetAddress("MIPS")); + require( + address(mips.oracle()) == mustGetAddress("PreimageOracle"), "DeployHoloceneUpgrade: invalid MIPS oracle" + ); + } + + /// @dev Deploys the Holocene `FaultDisputeGame` implementation contract. + function deployFaultDisputeGameImplementation() public { + bytes memory constructorInput = abi.encodeCall( + IFaultDisputeGame.__constructor__, + ( + GameTypes.CANNON, + Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())), + cfg.faultGameMaxDepth(), + cfg.faultGameSplitDepth(), + Duration.wrap(uint64(cfg.faultGameClockExtension())), + Duration.wrap(uint64(cfg.faultGameMaxClockDuration())), + IBigStepper(mustGetAddress("MIPS")), + IDelayedWETH(payable(mustGetAddress("DelayedWETHProxyFDG"))), + IAnchorStateRegistry(mustGetAddress("AnchorStateRegistry")), + cfg.l2ChainID() + ) + ); + + vm.broadcast(msg.sender); + address fdg = DeployUtils.create1("FaultDisputeGame", DeployUtils.encodeConstructor(constructorInput)); + save("FaultDisputeGame", fdg); + } + + /// @dev Checks if the `FaultDisputeGame` contract is correctly configured. + function checkFaultDisputeGame() public view { + IFaultDisputeGame fdg = IFaultDisputeGame(mustGetAddress("FaultDisputeGame")); + require( + fdg.gameType().raw() == GameTypes.CANNON.raw(), "DeployHoloceneUpgrade: invalid FaultDisputeGame gameType" + ); + require( + fdg.absolutePrestate().raw() == bytes32(cfg.faultGameAbsolutePrestate()), + "DeployHoloceneUpgrade: invalid FaultDisputeGame absolutePrestate" + ); + require( + fdg.maxGameDepth() == cfg.faultGameMaxDepth(), "DeployHoloceneUpgrade: invalid FaultDisputeGame maxDepth" + ); + require( + fdg.splitDepth() == cfg.faultGameSplitDepth(), "DeployHoloceneUpgrade: invalid FaultDisputeGame splitDepth" + ); + require( + fdg.clockExtension().raw() == cfg.faultGameClockExtension(), + "DeployHoloceneUpgrade: invalid FaultDisputeGame clockExtension" + ); + require( + fdg.maxClockDuration().raw() == cfg.faultGameMaxClockDuration(), + "DeployHoloceneUpgrade: invalid FaultDisputeGame maxClockDuration" + ); + require(address(fdg.vm()) == mustGetAddress("MIPS"), "DeployHoloceneUpgrade: invalid FaultDisputeGame MIPS"); + require( + address(fdg.weth()) == mustGetAddress("DelayedWETHProxyFDG"), + "DeployHoloceneUpgrade: invalid FaultDisputeGame DelayedWETH" + ); + require( + address(fdg.anchorStateRegistry()) == mustGetAddress("AnchorStateRegistry"), + "DeployHoloceneUpgrade: invalid FaultDisputeGame AnchorStateRegistry" + ); + require(fdg.l2ChainId() == cfg.l2ChainID(), "DeployHoloceneUpgrade: invalid FaultDisputeGame l2ChainID"); + } + + /// @dev Deploys the Holocene `PermissionedDisputeGame` implementation contract. + function deployPermissionedDisputeGameImplementation() public { + bytes memory constructorInput = abi.encodeCall( + IPermissionedDisputeGame.__constructor__, + ( + GameTypes.PERMISSIONED_CANNON, + Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())), + cfg.faultGameMaxDepth(), + cfg.faultGameSplitDepth(), + Duration.wrap(uint64(cfg.faultGameClockExtension())), + Duration.wrap(uint64(cfg.faultGameMaxClockDuration())), + IBigStepper(mustGetAddress("MIPS")), + IDelayedWETH(payable(mustGetAddress("DelayedWETHProxyPDG"))), + IAnchorStateRegistry(mustGetAddress("AnchorStateRegistry")), + cfg.l2ChainID(), + cfg.l2OutputOracleProposer(), + cfg.l2OutputOracleChallenger() + ) + ); + + vm.broadcast(msg.sender); + address fdg = DeployUtils.create1("PermissionedDisputeGame", DeployUtils.encodeConstructor(constructorInput)); + save("PermissionedDisputeGame", fdg); + } + + /// @dev Checks if the `PermissionedDisputeGame` contract is correctly configured. + function checkPermissionedDisputeGame() public view { + IPermissionedDisputeGame pdg = IPermissionedDisputeGame(mustGetAddress("PermissionedDisputeGame")); + require( + pdg.gameType().raw() == GameTypes.PERMISSIONED_CANNON.raw(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame gameType" + ); + require( + pdg.absolutePrestate().raw() == bytes32(cfg.faultGameAbsolutePrestate()), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame absolutePrestate" + ); + require( + pdg.maxGameDepth() == cfg.faultGameMaxDepth(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame maxDepth" + ); + require( + pdg.splitDepth() == cfg.faultGameSplitDepth(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame splitDepth" + ); + require( + pdg.clockExtension().raw() == cfg.faultGameClockExtension(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame clockExtension" + ); + require( + pdg.maxClockDuration().raw() == cfg.faultGameMaxClockDuration(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame maxClockDuration" + ); + require( + address(pdg.vm()) == mustGetAddress("MIPS"), "DeployHoloceneUpgrade: invalid PermissionedDisputeGame MIPS" + ); + require( + address(pdg.weth()) == mustGetAddress("DelayedWETHProxyPDG"), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame DelayedWETH" + ); + require( + address(pdg.anchorStateRegistry()) == mustGetAddress("AnchorStateRegistry"), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame AnchorStateRegistry" + ); + require(pdg.l2ChainId() == cfg.l2ChainID(), "DeployHoloceneUpgrade: invalid PermissionedDisputeGame l2ChainID"); + require( + pdg.proposer() == cfg.l2OutputOracleProposer(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame proposer" + ); + require( + pdg.challenger() == cfg.l2OutputOracleChallenger(), + "DeployHoloceneUpgrade: invalid PermissionedDisputeGame challenger" + ); + } + + /// @dev Deploys a new implementation of the `DelayedWETH` contract. + function deployDelayedWETH() public { + uint256 delay = cfg.faultGameWithdrawalDelay(); + + vm.broadcast(msg.sender); + address impl = DeployUtils.create1({ + _name: "DelayedWETH", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IDelayedWETH.__constructor__, (delay))) + }); + + // Save the new implementation address. + save("DelayedWETH", impl); + } + + /// @dev Deploys a new proxy contract with a new `DelayedWETH` implementation. + function deployDelayedWETHProxy(string memory _variant) public { + address delayedWethOwner = cfg.finalSystemOwner(); + address proxyAdmin = mustGetAddress("ProxyAdmin"); + address impl = mustGetAddress("DelayedWETH"); + ISuperchainConfig superchainConfig = ISuperchainConfig(mustGetAddress("SuperchainConfig")); + string memory finalName = string.concat("DelayedWETHProxy", _variant); + + // Deploy the implementation and proxy contracts. + vm.broadcast(msg.sender); + IProxy proxy = IProxy( + DeployUtils.create1({ + _name: "Proxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxy.__constructor__, (msg.sender))) + }) + ); + + // Upgrade the proxy to the implementation and initialize it. + vm.broadcast(msg.sender); + proxy.upgradeToAndCall(impl, abi.encodeCall(IDelayedWETH.initialize, (delayedWethOwner, superchainConfig))); + + // Transfer the admin role of the proxy to the ProxyAdmin, now that we've upgraded + // and initialized the proxy. + vm.broadcast(msg.sender); + proxy.changeAdmin(proxyAdmin); + + // Save the proxy address. + save(finalName, address(proxy)); + } + + /// @dev Checks if the `DelayedWETH` contract is correctly configured. + function checkDelayedWETH(string memory _variant) internal { + string memory finalName = string.concat("DelayedWETHProxy", _variant); + IDelayedWETH delayedWeth = IDelayedWETH(mustGetAddress(finalName)); + require( + delayedWeth.delay() == cfg.faultGameWithdrawalDelay(), "DeployHoloceneUpgrade: invalid DelayedWETH delay" + ); + require( + delayedWeth.config() == ISuperchainConfig(mustGetAddress("SuperchainConfig")), + "DeployHoloceneUpgrade: invalid DelayedWETH config" + ); + + vm.prank(mustGetAddress("ProxyAdmin")); + address admin = IProxy(payable(address(delayedWeth))).admin(); + require(admin == mustGetAddress("ProxyAdmin"), "DeployHoloceneUpgrade: invalid DelayedWETH admin"); + } + + /// @dev Prints a summary of the deployment. + function printSummary() internal view { + console.log("1. SystemConfig: %s", mustGetAddress("SystemConfig")); + console.log("2. MIPS: %s", getAddress("MIPS")); + console.log("3. FaultDisputeGame: %s", getAddress("FaultDisputeGame")); + console.log("4. PermissionedDisputeGame: %s", getAddress("PermissionedDisputeGame")); + } +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/README.md new file mode 100644 index 0000000000..f4828cf3d1 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/README.md @@ -0,0 +1,62 @@ +# Holocene Upgrade + +This directory contains a repeatable task for: +* upgrading an `op-contracts/v1.6.0` deployment to `op-contracts/v1.8.0`. +* upgrading an `op-contracts/v1.3.0` deployment to `op-contracts/v1.8.0`, while retaining the `L2OutputOracle`. + +## Dependencies + +- [`docker`](https://docs.docker.com/engine/install/) +- [`just`](https://github.com/casey/just) +- [`foundry`](https://getfoundry.sh/) + +## Usage + +This script has several different modes of operation. Namely: +1. Deploy and upgrade `op-contracts/1.6.0` -> `op-contracts/v1.8.0` + - Always upgrade the `SystemConfig` + - FP options: + - With permissionless fault proofs enabled (incl. `FaultDisputeGame`) + - With permissioned fault proofs enabled (excl. `FaultDisputeGame`) +1. Deploy and upgrade `op-contracts/v1.3.0` -> `op-contracts/v1.8.0`, with the `L2OutputOracle` still active. + - Only upgrade the `SystemConfig` + +```sh +# 1. Clone the monorepo and navigate to this directory. +git clone --branch proposal/op-contracts/v1.8.0 --depth 1 git@github.com:ethereum-optimism/monorepo.git && \ + cd monorepo/packages/contracts-bedrock/scripts/upgrades/holocene + +# 2. Set up the `.env` file +# +# Read the documentation carefully, and when in doubt, reach out to the OP Labs team. +cp .env.example .env && vim .env + +# 3. Build the upgrade script Docker image +just build-image + +# 4. Run the upgrade task. +# +# This task will: +# - Deploy the new smart contract implementations. +# - Optionally, generate a safe upgrade bundle. +# - Optionally, generate a `superchain-ops` upgrade task. +# +# The first argument must be the absolute path to your deploy-config.json. +# You can optionally specify an output folder path different from the default `output/` as a +# second argument to `just run`, also as an absolute path. +just run $(realpath path/to/deploy-config.json) +``` + +Note that in order to build the Docker image, you have to allow Docker to use at least 16GB of +memory, or the Solidity compilations may fail. Docker's default is only 8GB. + +:warning: The `deploy-config.json` that you use for your chain must set the latest `faultGameAbsolutePrestate` +value, not the original value that was set during deployment of the chain. + +You can use `0x03f89406817db1ed7fd8b31e13300444652cdb0b9c509a674de43483b2f83568`, which is based on +`op-program/v1.4.0-rc.3` and includes Holocene activations for +* Sepolia: Base, OP, Metal, Mode, Zora, Ethernity, Unichain, Ink +* Mainnet: Base, OP, Orderly, Lyra, Metal, Mode, Zora, Lisk, Ethernity, Binary + +If you want to make local modifications to the scripts in `scripts/`, you need to build the Docker +image again with `just build-image` before running `just run`. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/justfile b/packages/contracts-bedrock/scripts/upgrades/holocene/justfile new file mode 100644 index 0000000000..a22c001c06 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/justfile @@ -0,0 +1,31 @@ +set dotenv-load # to have OP_CONTRACTS_RELEASE available for build-image + +# Default recipe to list help menu. +default: + @just --list + +# Run the deployment / upgrade generation image. If the image is not present locally, +# it will be built. +run deploy-config-path output-folder-path="$(pwd)/output/" *args='': + #!/bin/bash + if [ ! "$(docker images -q op-holocene-upgrade:local 2> /dev/null)" ]; then + just build-image + fi + + mkdir -p {{output-folder-path}} + + # Run the deployment. + docker run -it \ + --rm \ + -v {{output-folder-path}}:/output \ + -v {{deploy-config-path}}:/app/packages/contracts-bedrock/deploy-config/deploy-config.json \ + --env-file=.env \ + op-holocene-upgrade:local {{args}} + +# Build the image locally. +build-image: + docker build \ + -t op-holocene-upgrade:local \ + -f upgrade.dockerfile \ + --build-arg REV=op-contracts/${OP_CONTRACTS_RELEASE} \ + . diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh new file mode 100755 index 0000000000..a630432246 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/common.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Cache dir to store fetched TOML files +declare CACHE_DIR + +# error_handler +# +# Basic error handler +error_handler() { + echo "Error occurred in ${BASH_SOURCE[1]} at line: ${BASH_LINENO[0]}" + echo "Error message: $BASH_COMMAND" + exit 1 +} + +# Register the error handler +trap error_handler ERR + +# reqenv +# +# Checks if a specified environment variable is set. +# +# Arguments: +# $1 - The name of the environment variable to check +# +# Exits with status 1 if: +# - The specified environment variable is not set +reqenv() { + if [ -z "$1" ]; then + echo "Error: $1 is not set" + exit 1 + fi +} + +# prompt +# +# Prompts the user for a yes/no response. +# +# Arguments: +# $1 - The prompt message +# +# Exits with status 1 if: +# - The user does not respond with 'y' +# - The process is interrupted +prompt() { + read -p "$1 [Y/n] " -n 1 -r + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + [[ "$0" = "${BASH_SOURCE[0]}" ]] && exit 1 || return 1 + exit 1 + fi +} + +# fetch_standard_address +# +# Fetches the implementation address for a given contract from a TOML file. +# The TOML file is downloaded from a URL specified in ADDRESSES_TOML_URL +# environment variable. Results are cached to avoid repeated downloads. +# +# Arguments: +# $1 - Network name +# $2 - The release version +# $3 - The name of the contract to look up +# +# Returns: +# The implementation address of the specified contract +# +# Exits with status 1 if: +# - Failed to fetch the TOML file +# - The release version is not found in the TOML file +# - The implementation address for the specified contract is not found +fetch_standard_address() { + local network_name="$1" + local release_version="$2" + local contract_name="$3" + + if [[ "$network_name" != "mainnet" && "$network_name" != "sepolia" ]]; then + echo "Error: NETWORK must be set to 'mainnet' or 'sepolia'" + exit 1 + fi + + # Ensure cache dir exists + CACHE_DIR="${CACHE_DIR:-$(mktemp -d)}" + + local toml_path="${CACHE_DIR}/standard-versions-$network_name.toml" + if [[ ! -f "$toml_path" ]]; then + local toml_url="https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/refs/heads/main/validation/standard/standard-versions-$network_name.toml" + if ! curl -s "$toml_url" -o "$toml_path"; then + echo "Error: Failed to fetch TOML file from $toml_url" + exit 1 + fi + fi + + local contract_path=".releases.\"op-contracts/${release_version}\".$contract_name" + local contract_address + contract_address=$(yq "${contract_path}.address // ${contract_path}.implementation_address // \"\"" "${toml_path}") + if [[ -z "$contract_address" ]]; then + echo "Error: Implementation address for $contract_name not found in $release_version release" + exit 1 + fi + echo "${contract_address}" +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh new file mode 100755 index 0000000000..d1e15aa63e --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/deploy.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check required environment variables +reqenv "ETH_RPC_URL" +reqenv "PRIVATE_KEY" +reqenv "ETHERSCAN_API_KEY" +reqenv "DEPLOY_CONFIG_PATH" +reqenv "IMPL_SALT" + +# Check required address environment variables +reqenv "PROXY_ADMIN_ADDR" +reqenv "SUPERCHAIN_CONFIG_PROXY_ADDR" +reqenv "PREIMAGE_ORACLE_ADDR" +reqenv "ANCHOR_STATE_REGISTRY_PROXY_ADDR" +reqenv "DELAYED_WETH_IMPL_ADDR" +reqenv "SYSTEM_CONFIG_IMPL_ADDR" +reqenv "MIPS_IMPL_ADDR" +reqenv "USE_FAULT_PROOFS" +reqenv "USE_PERMISSIONLESS_FAULT_PROOFS" + +# Run the upgrade script +forge script DeployUpgrade.s.sol \ + --rpc-url "$ETH_RPC_URL" \ + --private-key "$PRIVATE_KEY" \ + --etherscan-api-key "$ETHERSCAN_API_KEY" \ + --sig "deploy(address,address,address,address,address,address,address,bool,bool)" \ + "$PROXY_ADMIN_ADDR" \ + "$SUPERCHAIN_CONFIG_PROXY_ADDR" \ + "$SYSTEM_CONFIG_IMPL_ADDR" \ + "$MIPS_IMPL_ADDR" \ + "$DELAYED_WETH_IMPL_ADDR" \ + "$PREIMAGE_ORACLE_ADDR" \ + "$ANCHOR_STATE_REGISTRY_PROXY_ADDR" \ + "$USE_FAULT_PROOFS" \ + "$USE_PERMISSIONLESS_FAULT_PROOFS" \ + --broadcast \ + --verify \ + --slow diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh new file mode 100755 index 0000000000..0f57634459 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +echo " +⠄⢀⠀⠀⡐⠠⠀⢂⣠⡤⣤⣖⣤⣤⣄⢢⣤⣤⣤⡀⠄⠰⠀⠆⠀⠀⠠⠀⠆⠠⢀⠢⠐⠆⡀ +⠔⢀⠀⠄⡀⠔⢠⣾⣿⣿⣷⣿⣿⣿⣿⣷⣟⣛⣻⣿⣤⡠⢀⠆⢄⠂⠠⢠⠀⠄⠀⢠⠰⢄⠄ +⣈⠀⠐⡀⠀⠈⣾⣿⣿⣿⣿⡿⡿⣿⣿⣿⣿⡿⢿⣿⣿⣿⣦⠀⠂⠀⢈⠀⠁⠂⡀⢀⠠⠈⡀ +⠆⠈⠀⠄⣃⣼⣿⣿⣿⠿⡿⣿⣿⣷⣾⣷⣾⡾⣿⣿⣿⢿⡟⡇⠠⠁⠨⠐⠀⠃⠱⠊⠀⠀⠄ +⠐⣠⣶⣿⣿⣿⣿⣿⣿⣿⣾⣮⣛⢿⣿⡿⠘⣇⢯⣹⣶⣷⣿⣿⡄⠆⢐⠀⢄⠢⡒⠐⠠⢀⠂ +⢴⣿⣿⣿⣿⣿⣿⡿⠿⢿⣿⣿⣟⢿⣶⣾⣿⣧⢻⣿⢿⣿⣿⣿⠂⠈⢀⠈⡀⡁⢀⠉⢀⠀⠀ +⡜⣿⣿⣿⣿⣿⣿⣦⡻⠀⢨⣽⣿⣿⣿⣿⣿⣿⣦⡛⣾⣭⣃⣀⣦⠀⠨⠐⠀⠄⠃⠈⠀⠀⠁ +⣳⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⢛⣭⣿⣿⣿⣿⣿⣿⣿⢢⣿⣿⣿⣿⡇⢀⠲⠂⠄⢀⡐⠠⠐⠂ +⢣⠵⢹⣿⣿⣿⣿⣿⣿⣿⣿⣧⣛⣻⣿⣭⣿⣿⠻⢥⣿⣿⣿⣿⣿⣿⡄⠀⠁⡀⢀⢈⠀⢀⠁ +⡼⣹⡘⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣟⣻⠿⠿⣿⣿⣿⣿⣿⣿⣿⣿⠇⠀⠀⠁⠉⠐⠀⠂⠈ +⡷⣡⠧⢹⣿⣿⣿⣿⣿⣿⣗⣶⣾⣿⣿⣿⣿⣿⠮⢿⣿⣿⡿⠿⣿⣿⢀⣀⠂⡁⠐⢌⠐⡠⠁ +⢷⠡⠏⢧⢌⢻⣿⣿⣿⣟⣿⣿⣻⣿⡿⠿⣛⣵⢟⣭⡭⠥⠮⠕⣒⣒⣚⣮⣰⢤⣤⣄⣀⡠⠄ +⠁⠠⠀⠄⣏⢦⡙⣿⣿⣽⣟⣿⡟⣬⣾⣿⣿⣿⣾⣆⠭⠽⠶⢶⢶⣖⣶⣹⣖⡿⣿⣿⣿⣿⡆ +⠀⡁⠂⡟⡜⣦⢫⣌⠻⣷⣿⢏⣾⣿⣿⣿⢿⣿⢿⣿⣿⣿⣿⣿⠿⣛⣛⣛⠛⣭⣿⣿⢻⣽⡇ +⣏⠖⣮⢱⣋⢖⣣⢎⡳⢤⡅⣾⣿⢯⣷⡏⡖⣰⣝⣚⣙⣫⢍⡶⠷⣽⢭⣛⡇⠀⢰⣶⣾⣿⠀ +⡮⡝⢦⣓⢎⡳⡜⠎⠡⠁⢸⣿⣿⣿⣿⣁⢠⣿⡿⣿⡟⣎⣯⣽⣋⡷⣾⣹⡃⠀⢸⣿⣿⢿⠀ +⣳⠁⠀⡝⢮⣱⢹⠀⠂⠈⣿⣿⣿⣿⡻⣈⣸⣿⣙⢾⢿⣹⡶⣿⣼⣗⣻⡞⣡⠁⣼⣿⣿⣿⡀ +⡔⢦⢥⡛⣜⠦⣏⡄⡈⣸⢿⣿⡿⣽⢃⠇⣿⣧⡝⣟⡳⢾⣹⣟⡻⣾⣹⢣⠞⣄⣯⣿⣷⣿⡆ + -*~ [ Grug Deployer mk2 ] ~*- + ~*- [ Holocene ] -*~ +" + +# Set variables from environment or error. +export RELEASE=${OP_CONTRACTS_RELEASE:?OP_CONTRACTS_RELEASE must be set} +export NETWORK="${NETWORK:?NETWORK must be set}" +export ETHERSCAN_API_KEY=${ETHERSCAN_API_KEY:?ETHERSCAN_API_KEY must be set} +export ETH_RPC_URL=${ETH_RPC_URL:?ETH_RPC_URL must be set} +export PRIVATE_KEY=${PRIVATE_KEY:?PRIVATE_KEY must be set} +export OUTPUT_FOLDER_PATH="/output" +export SYSTEM_CONFIG_IMPL_ADDR=${SYSTEM_CONFIG_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "system_config")} +export MIPS_IMPL_ADDR=${MIPS_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "mips")} +export PREIMAGE_ORACLE_ADDR=${PREIMAGE_ORACLE_ADDR:?PREIMAGE_ORACLE_ADDR must be set} +export ANCHOR_STATE_REGISTRY_PROXY_ADDR=${ANCHOR_STATE_REGISTRY_PROXY_ADDR:?ANCHOR_STATE_REGISTRY_PROXY_ADDR must be set} +export DELAYED_WETH_IMPL_ADDR=${DELAYED_WETH_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "delayed_weth")} +export PROXY_ADMIN_ADDR=${PROXY_ADMIN_ADDR:?PROXY_ADMIN_ADDR must be set} +export SUPERCHAIN_CONFIG_PROXY_ADDR=${SUPERCHAIN_CONFIG_PROXY_ADDR:?SUPERCHAIN_CONFIG_ADDR must be set} +export SYSTEM_CONFIG_PROXY_ADDR=${SYSTEM_CONFIG_PROXY_ADDR:?SYSTEM_CONFIG_PROXY_ADDR must be set} +export DISPUTE_GAME_FACTORY_PROXY_ADDR=${DISPUTE_GAME_FACTORY_PROXY_ADDR:?DISPUTE_GAME_FACTORY_PROXY_ADDR must be set} +export USE_FAULT_PROOFS=${USE_FAULT_PROOFS:?USE_FAULT_PROOFS must be set} +export USE_PERMISSIONLESS_FAULT_PROOFS=${USE_PERMISSIONLESS_FAULT_PROOFS:?USE_PERMISSIONLESS_FAULT_PROOFS must be set} + +# Sanity check FP configuration. +if [[ $USE_PERMISSIONLESS_FAULT_PROOFS == true && $USE_FAULT_PROOFS == false ]]; then + echo "Error: USE_PERMISSIONLESS_FAULT_PROOFS cannot be true if USE_FAULT_PROOFS is false" + exit 1 +fi + +# Make the output folder, if it doesn't exist +mkdir -p "$OUTPUT_FOLDER_PATH" + +# Find the contracts-bedrock directory +CONTRACTS_BEDROCK_DIR=$(pwd) +while [[ "$CONTRACTS_BEDROCK_DIR" != "/" && "${CONTRACTS_BEDROCK_DIR##*/}" != "contracts-bedrock" ]]; do + CONTRACTS_BEDROCK_DIR=$(dirname "$CONTRACTS_BEDROCK_DIR") +done + +# Error out if we couldn't find it for some reason +if [[ "$CONTRACTS_BEDROCK_DIR" == "/" ]]; then + echo "Error: 'contracts-bedrock' directory not found" + exit 1 +fi + +# The deploy config is mounted via Docker to this file +export DEPLOY_CONFIG_PATH="$CONTRACTS_BEDROCK_DIR/deploy-config/deploy-config.json" + +# Run deploy.sh if deployments.json does not exist +DEPLOY_LOG_PATH="$OUTPUT_FOLDER_PATH/deploy.log" +DEPLOYMENTS_JSON_PATH="$OUTPUT_FOLDER_PATH/deployments.json" +if [[ ! -f "$DEPLOYMENTS_JSON_PATH" ]]; then + if ! "$SCRIPT_DIR/deploy.sh" | tee "$DEPLOY_LOG_PATH"; then + echo "Error: deploy.sh failed" + exit 1 + fi +else + prompt "Skipping deployment as $DEPLOYMENTS_JSON_PATH already exists. Continue?" +fi + +# Extract the addresses from the deployment logs +# shellcheck disable=2155 +export SYSTEM_CONFIG_IMPL=$(grep "1. SystemConfig:" "$DEPLOY_LOG_PATH" | awk '{print $3}') +# shellcheck disable=2155 +export MIPS_IMPL=$(grep "2. MIPS:" "$DEPLOY_LOG_PATH" | awk '{print $3}') +# shellcheck disable=2155 +export FDG_IMPL=$(grep "3. FaultDisputeGame:" "$DEPLOY_LOG_PATH" | awk '{print $3}') +# shellcheck disable=2155 +export PDG_IMPL=$(grep "4. PermissionedDisputeGame:" "$DEPLOY_LOG_PATH" | awk '{print $3}') + +# Ensure that the addresses were extracted properly +reqenv "SYSTEM_CONFIG_IMPL" +reqenv "MIPS_IMPL" +reqenv "FDG_IMPL" +reqenv "PDG_IMPL" + +# Generate deployments.json with extracted addresses +cat <"$DEPLOYMENTS_JSON_PATH" +{ + "SystemConfig": "$SYSTEM_CONFIG_IMPL", + "MIPS": "$MIPS_IMPL", + "FaultDisputeGame": "$FDG_IMPL", + "PermissionedDisputeGame": "$PDG_IMPL" +} +EOF + +echo "✨ Deployed contracts and saved addresses to \"$DEPLOYMENTS_JSON_PATH\"" + +# Print a message when the script exits +trap 'echo "✨ Done. Artifacts are available in \"$OUTPUT_FOLDER_PATH\""' EXIT + +prompt "Generate safe upgrade bundle for SystemConfig?" + +# Generate the system config upgrade bundle +if ! "$SCRIPT_DIR/sys-cfg-bundle.sh"; then + echo "Error: sys-cfg-bundle.sh failed" + exit 1 +fi + +prompt "Generate superchain-ops upgrade task for SystemConfig upgrade bundle?" + +# Generate the superchain-ops upgrade task +if ! "$SCRIPT_DIR/sc-ops-sys-cfg.sh"; then + echo "Error: sc-ops-sys-cfg.sh failed" + exit 1 +fi + +if [[ $USE_FAULT_PROOFS == true ]]; then + prompt "Generate safe upgrade bundle for proofs contracts?" + + # Generate the proofs contracts' upgrade bundle + if ! "$SCRIPT_DIR/proofs-bundle.sh"; then + echo "Error: proofs-bundle.sh failed" + exit 1 + fi + + prompt "Generate superchain-ops upgrade task for proofs contracts upgrade bundle?" + + # Generate the superchain-ops upgrade task + if ! "$SCRIPT_DIR/sc-ops-proofs.sh"; then + echo "Error: sc-ops-proofs.sh failed" + exit 1 + fi +fi diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh new file mode 100755 index 0000000000..bc0b0c7ff3 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check the env +reqenv "ETH_RPC_URL" +reqenv "OUTPUT_FOLDER_PATH" +reqenv "MIPS_IMPL" +reqenv "FDG_IMPL" +reqenv "PDG_IMPL" +reqenv "DISPUTE_GAME_FACTORY_PROXY_ADDR" +reqenv "USE_PERMISSIONLESS_FAULT_PROOFS" + +# Local environment +BUNDLE_PATH="$OUTPUT_FOLDER_PATH/proofs_bundle.json" +L1_CHAIN_ID=$(cast chain-id) + +# Copy the bundle template +cp ./templates/proof_upgrade_bundle_template.json "$BUNDLE_PATH" + +# Tx 1: Upgrade PermissionedDisputeGame implementation +TX_1_PAYLOAD=$(cast calldata "setImplementation(uint32,address)" 1 "$PDG_IMPL") + +# Tx 2: Upgrade FaultDisputeGame implementation +TX_2_PAYLOAD=$(cast calldata "setImplementation(uint32,address)" 0 "$FDG_IMPL") + +# Replace variables +sed -i "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH" +sed -i "s/\$PDG_IMPL/$PDG_IMPL/g" "$BUNDLE_PATH" +sed -i "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH" +sed -i "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH" + +# Conditionally, if the FDG is being deployed, append the bundle extension +if [ "$USE_PERMISSIONLESS_FAULT_PROOFS" == true ]; then + echo "✨ USE_PERMISSIONLESS_FAULT_PROOFS=true | Adding FDG deployment to upgrade bundle." + jq --argjson fdg_extension "$(cat ./templates/fdg_bundle_extension.json)" \ + '.transactions += [$fdg_extension]' \ + "$BUNDLE_PATH" >"$BUNDLE_PATH.tmp" + mv "$BUNDLE_PATH.tmp" "$BUNDLE_PATH" + + # Replace variables + sed -i "s/\$FDG_IMPL/$FDG_IMPL/g" "$BUNDLE_PATH" + sed -i "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH" +fi + +sed -i "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$BUNDLE_PATH" + +echo "✨ Generated proof contracts upgrade bundle at \"$BUNDLE_PATH\"" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh new file mode 100755 index 0000000000..ec386727d3 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check required environment variables +reqenv "OUTPUT_FOLDER_PATH" +reqenv "MIPS_IMPL" +reqenv "FDG_IMPL" +reqenv "PDG_IMPL" +reqenv "DISPUTE_GAME_FACTORY_PROXY_ADDR" + +# Create directory for the task +TASK_DIR="$OUTPUT_FOLDER_PATH/proofs-sc-ops-task" +mkdir -p "$TASK_DIR" + +# Copy the bundle and task template +cp "$OUTPUT_FOLDER_PATH/proofs_bundle.json" "$TASK_DIR/input.json" +cp -R "$SCRIPT_DIR/../templates/proofs-sc-ops-task/." "$TASK_DIR/" + +# Generate the task overview +msup render -i "$TASK_DIR/input.json" -o "$TASK_DIR/OVERVIEW.md" + +# Generate the README +sed -i "s/\$MIPS_IMPL/$MIPS_IMPL/g" "$TASK_DIR/README.md" +sed -i "s/\$FDG_IMPL/$FDG_IMPL/g" "$TASK_DIR/README.md" +sed -i "s/\$PDG_IMPL/$PDG_IMPL/g" "$TASK_DIR/README.md" + +# Generate the validation doc +OLD_FDG=$(cast call "$DISPUTE_GAME_FACTORY_PROXY_ADDR" "gameImpls(uint32)" 0) +OLD_PDG=$(cast call "$DISPUTE_GAME_FACTORY_PROXY_ADDR" "gameImpls(uint32)" 1) + +PADDED_OLD_FDG=$(cast 2u "$OLD_FDG") +PADDED_OLD_PDG=$(cast 2u "$OLD_PDG") +PADDED_FDG_IMPL=$(cast 2u "$FDG_IMPL") +PADDED_PDG_IMPL=$(cast 2u "$PDG_IMPL") + +sed -i "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$OLD_FDG/$PADDED_OLD_FDG/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$FDG_IMPL/$PADDED_FDG_IMPL/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$PDG_IMPL/$PADDED_PDG_IMPL/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$OLD_PDG/$PADDED_OLD_PDG/g" "$TASK_DIR/VALIDATION.md" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh new file mode 100755 index 0000000000..a87de445d3 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check required environment variables +reqenv "OUTPUT_FOLDER_PATH" +reqenv "SYSTEM_CONFIG_IMPL" +reqenv "SYSTEM_CONFIG_PROXY_ADDR" + +# Create directory for the task +TASK_DIR="$OUTPUT_FOLDER_PATH/sys-cfg-sc-ops-task" +mkdir -p "$TASK_DIR" + +# Copy the bundle and task template +cp "$OUTPUT_FOLDER_PATH/sys_cfg_bundle.json" "$TASK_DIR/input.json" +cp -R "$SCRIPT_DIR/../templates/sys-cfg-sc-ops-task/." "$TASK_DIR/" + +# Generate the task overview +msup render -i "$TASK_DIR/input.json" -o "$TASK_DIR/OVERVIEW.md" + +# Generate the README +sed -i "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$TASK_DIR/README.md" + +# Generate the validation doc +OLD_SYS_CFG=$(cast impl "$SYSTEM_CONFIG_PROXY_ADDR") + +PADDED_OLD_SYS_CFG=$(cast 2u "$OLD_SYS_CFG") +PADDED_SYS_CFG=$(cast 2u "$SYSTEM_CONFIG_IMPL") + +sed -i "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$OLD_SYS_CFG/$PADDED_OLD_SYS_CFG/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$SYSTEM_CONFIG_IMPL/$PADDED_SYS_CFG/g" "$TASK_DIR/VALIDATION.md" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh new file mode 100755 index 0000000000..bde4917e6b --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Grab the script directory +SCRIPT_DIR=$(dirname "$0") + +# Load common.sh +# shellcheck disable=SC1091 +source "$SCRIPT_DIR/common.sh" + +# Check the env +reqenv "ETH_RPC_URL" +reqenv "OUTPUT_FOLDER_PATH" +reqenv "PROXY_ADMIN_ADDR" +reqenv "SYSTEM_CONFIG_PROXY_ADDR" +reqenv "SYSTEM_CONFIG_IMPL" + +# Local environment +BUNDLE_PATH="$OUTPUT_FOLDER_PATH/sys_cfg_bundle.json" +L1_CHAIN_ID=$(cast chain-id) + +# Copy the bundle template +cp ./templates/sys_cfg_upgrade_bundle_template.json "$BUNDLE_PATH" + +# Tx 1: Upgrade SystemConfigProxy implementation +TX_1_PAYLOAD=$(cast calldata "upgrade(address,address)" "$SYSTEM_CONFIG_PROXY_ADDR" "$SYSTEM_CONFIG_IMPL") + +# Replace variables +sed -i "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH" +sed -i "s/\$PROXY_ADMIN_ADDR/$PROXY_ADMIN_ADDR/g" "$BUNDLE_PATH" +sed -i "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$BUNDLE_PATH" +sed -i "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$BUNDLE_PATH" +sed -i "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH" + +echo "✨ Generated SystemConfig upgrade bundle at \"$BUNDLE_PATH\"" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json new file mode 100644 index 0000000000..fa6680496b --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/fdg_bundle_extension.json @@ -0,0 +1,29 @@ +{ + "metadata": { + "name": "Upgrade `CANNON` game type in `DisputeGameFactory`", + "description": "Upgrades the `CANNON` game type to the new Holocene deployment, with an updated version of `op-program` as the absolute prestate hash." + }, + "to": "$DISPUTE_GAME_FACTORY_PROXY_ADDR", + "value": "0x0", + "data": "$TX_2_PAYLOAD", + "contractMethod": { + "type": "function", + "name": "setImplementation", + "inputs": [ + { + "name": "_gameType", + "type": "uint32" + }, + { + "name": "_impl", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + "contractInputsValues": { + "_gameType": "0", + "_impl": "$FDG_IMPL" + } +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json new file mode 100644 index 0000000000..e74360f02a --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proof_upgrade_bundle_template.json @@ -0,0 +1,38 @@ +{ + "chainId": $L1_CHAIN_ID, + "metadata": { + "name": "Holocene Hardfork - Proof Contract Upgrades", + "description": "Upgrades the `MIPS.sol`, `FaultDisputeGame.sol`, and `PermissionedDisputeGame.sol` contracts for Holocene." + }, + "transactions": [ + { + "metadata": { + "name": "Upgrade `PERMISSIONED_CANNON` game type in `DisputeGameFactory`", + "description": "Upgrades the `PERMISSIONED_CANNON` game type to the new Holocene deployment, with an updated version of `op-program` as the absolute prestate hash." + }, + "to": "$DISPUTE_GAME_FACTORY_PROXY_ADDR", + "value": "0x0", + "data": "$TX_1_PAYLOAD", + "contractMethod": { + "type": "function", + "name": "setImplementation", + "inputs": [ + { + "name": "_gameType", + "type": "uint32" + }, + { + "name": "_impl", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + "contractInputsValues": { + "_gameType": "1", + "_impl": "$PDG_IMPL" + } + } + ] +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example new file mode 100644 index 0000000000..7762f5b6dd --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/.env.example @@ -0,0 +1,6 @@ +ETH_RPC_URL= +COUNCIL_SAFE= +FOUNDATION_SAFE= +OWNER_SAFE= +SAFE_NONCE= +SIMULATE_WITHOUT_LEDGER=0 # set to 1 during development diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md new file mode 100644 index 0000000000..a5a582bc24 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/README.md @@ -0,0 +1,46 @@ +# Holocene Hardfork Upgrade + +Status: DRAFT, NOT READY TO SIGN + +## Objective + +Upgrades the Fault Proof contracts for the Holocene hardfork. + +The proposal was: + +- [ ] Posted on the governance forum. +- [ ] Approved by Token House voting. +- [ ] Not vetoed by the Citizens' house. +- [ ] Executed on OP Mainnet. + +The governance proposal should be treated as the source of truth and used to verify the correctness of the onchain operations. + +Governance post of the upgrade can be found at . + +This upgrades the Fault Proof contracts in the +[op-contracts/v1.8.0-rc.4](https://github.com/ethereum-optimism/optimism/tree/op-contracts/v1.8.0-rc.4) release. + +## Pre-deployments + +- `MIPS` - `$MIPS_IMPL` +- `FaultDisputeGame` - `$FDG_IMPL` +- `PermissionedDisputeGame` - `$PDG_IMPL` + +## Simulation + +Please see the "Simulating and Verifying the Transaction" instructions in [NESTED.md](../../../NESTED.md). +When simulating, ensure the logs say `Using script /your/path/to/superchain-ops/tasks//NestedSignFromJson.s.sol`. +This ensures all safety checks are run. If the default `NestedSignFromJson.s.sol` script is shown (without the full path), something is wrong and the safety checks will not run. + +## State Validation + +Please see the instructions for [validation](./VALIDATION.md). + +## Execution + +This upgrade +* Changes dispute game implementation of the `CANNON` and `PERMISSIONED_CANNON` game types to contain a `op-program` release for the Holocene hardfork, which contains + the Holocene fork implementation as well as a `ChainConfig` and `RollupConfig` for the L2 chain being upgraded. +* Upgrades `MIPS.sol` to support the `F_GETFD` syscall, required by the golang 1.22+ runtime. + +See the [overview](./OVERVIEW.md) and `input.json` bundle for more details. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md new file mode 100644 index 0000000000..85a51eddc8 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/proofs-sc-ops-task/VALIDATION.md @@ -0,0 +1,26 @@ +# Validation + +This document can be used to validate the state diff resulting from the execution of the upgrade +transaction. + +For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract: + +- The following state changes (and none others) are made to that contract. This validates that no unexpected state changes occur. +- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic. +- All key values match the semantic meaning provided, which can be validated using the storage layout links provided. + +## State Changes + +### `$DISPUTE_GAME_FACTORY_PROXY_ADDR` (`DisputeGameFactoryProxy`) + +- **Key**: `0xffdfc1249c027f9191656349feb0761381bb32c9f557e01f419fd08754bf5a1b`
+ **Before**: `$OLD_FDG`
+ **After**: `$FDG_IMPL`
+ **Meaning**: Updates the CANNON game type implementation. You can verify which implementation is set using `cast call $DISPUTE_GAME_FACTORY_PROXY_ADDR "gameImpls(uint32)(address)" 0`, where `0` is the [`CANNON` game type](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.4.0/packages/contracts-bedrock/src/dispute/lib/Types.sol#L28). + Before this task has been executed, you will see that the returned address is `$OLD_FDG`, matching the "Before" value of this slot, demonstrating this slot is storing the address of the CANNON implementation. + +- **Key**: `0x4d5a9bd2e41301728d41c8e705190becb4e74abe869f75bdb405b63716a35f9e`
+ **Before**: `$OLD_PDG`
+ **After**: `$PDG_IMPL`
+ **Meaning**: Updates the PERMISSIONED_CANNON game type implementation. You can verify which implementation is set using `cast call $DISPUTE_GAME_FACTORY_PROXY_ADDR "gameImpls(uint32)(address)" 1`, where `1` is the [`PERMISSIONED_CANNON` game type](https://github.com/ethereum-optimism/optimism/blob/op-contracts/v1.4.0/packages/contracts-bedrock/src/dispute/lib/Types.sol#L31). + Before this task has been executed, you will see that the returned address is `$OLD_PDG`, matching the "Before" value of this slot, demonstrating this slot is storing the address of the PERMISSIONED_CANNON implementation. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example new file mode 100644 index 0000000000..7762f5b6dd --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/.env.example @@ -0,0 +1,6 @@ +ETH_RPC_URL= +COUNCIL_SAFE= +FOUNDATION_SAFE= +OWNER_SAFE= +SAFE_NONCE= +SIMULATE_WITHOUT_LEDGER=0 # set to 1 during development diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md new file mode 100644 index 0000000000..740219586b --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/README.md @@ -0,0 +1,42 @@ +# Holocene Hardfork Upgrade - `SystemConfig` + +Status: DRAFT, NOT READY TO SIGN + +## Objective + +Upgrades the `SystemConfig` for the Holocene hardfork. + +The proposal was: + +- [ ] Posted on the governance forum. +- [ ] Approved by Token House voting. +- [ ] Not vetoed by the Citizens' house. +- [ ] Executed on OP Mainnet. + +The governance proposal should be treated as the source of truth and used to verify the correctness of the onchain operations. + +Governance post of the upgrade can be found at . + +This upgrades the `SystemConfig` in the +[op-contracts/v1.8.0](https://github.com/ethereum-optimism/optimism/tree/op-contracts/v1.8.0-rc.1) release. + +## Pre-deployments + +- `SystemConfig` - `$SYSTEM_CONFIG_IMPL` + +## Simulation + +Please see the "Simulating and Verifying the Transaction" instructions in [NESTED.md](../../../NESTED.md). +When simulating, ensure the logs say `Using script /your/path/to/superchain-ops/tasks//NestedSignFromJson.s.sol`. +This ensures all safety checks are run. If the default `NestedSignFromJson.s.sol` script is shown (without the full path), something is wrong and the safety checks will not run. + +## State Validation + +Please see the instructions for [validation](./VALIDATION.md). + +## Execution + +This upgrade +* Changes the implementation of the `SystemConfig` to hold EIP-1559 parameters for the + +See the [overview](./OVERVIEW.md) and `input.json` bundle for more details. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md new file mode 100644 index 0000000000..3a5e7b6759 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys-cfg-sc-ops-task/VALIDATION.md @@ -0,0 +1,19 @@ +# Validation + +This document can be used to validate the state diff resulting from the execution of the upgrade +transaction. + +For each contract listed in the state diff, please verify that no contracts or state changes shown in the Tenderly diff are missing from this document. Additionally, please verify that for each contract: + +- The following state changes (and none others) are made to that contract. This validates that no unexpected state changes occur. +- All addresses (in section headers and storage values) match the provided name, using the Etherscan and Superchain Registry links provided. This validates the bytecode deployed at the addresses contains the correct logic. +- All key values match the semantic meaning provided, which can be validated using the storage layout links provided. + +## State Changes + +### `$SYSTEM_CONFIG_PROXY_ADDR` (`SystemConfigProxy`) + +- **Key**: `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + **Before**: `$OLD_SYS_CFG` + **After**: `$SYSTEM_CONFIG_IMPL` + **Meaning**: Updates the `SystemConfig` proxy implementation. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json new file mode 100644 index 0000000000..62746a5198 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/templates/sys_cfg_upgrade_bundle_template.json @@ -0,0 +1,38 @@ +{ + "chainId": $L1_CHAIN_ID, + "metadata": { + "name": "Holocene Hardfork - SystemConfig Upgrade", + "description": "Upgrades the `SystemConfig.sol` contract for Holocene." + }, + "transactions": [ + { + "metadata": { + "name": "Upgrade `SystemConfig` proxy", + "description": "Upgrades the `SystemConfig` proxy to the new implementation, featuring configurable EIP-1559 parameters." + }, + "to": "$PROXY_ADMIN_ADDR", + "value": "0x0", + "data": "$TX_1_PAYLOAD", + "contractMethod": { + "type": "function", + "name": "upgrade", + "inputs": [ + { + "name": "_proxy", + "type": "address" + }, + { + "name": "_implementation", + "type": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + "contractInputsValues": { + "_proxy": "$SYSTEM_CONFIG_PROXY_ADDR", + "_implementation": "$SYSTEM_CONFIG_IMPL" + } + } + ] +} diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile b/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile new file mode 100644 index 0000000000..a675ab9189 --- /dev/null +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile @@ -0,0 +1,66 @@ +# Use a base image with necessary tools +FROM ubuntu:20.04 + +ARG REV + +# Install required packages +RUN apt-get update && apt-get install -y \ + git \ + bash \ + curl \ + build-essential \ + jq \ + && rm -rf /var/lib/apt/lists/* + +# Install Rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +# Install just +RUN curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to /usr/local/bin + +# Install yq +RUN curl --proto '=https' --tlsv1.2 -sSf -L https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -o /usr/bin/yq && \ + chmod +x /usr/bin/yq + +# Install msup +RUN git clone --depth 1 https://github.com/clabby/msup.git && \ + cd msup && \ + cargo install --path . + +# Install foundryup +RUN curl --proto '=https' --tlsv1.2 -sSf -L https://foundry.paradigm.xyz | bash +ENV PATH="/root/.foundry/bin:${PATH}" + +# Set the working directory +WORKDIR /app + +# Clone the repository, only at the target revision +RUN git clone --branch $REV --depth 1 https://github.com/ethereum-optimism/optimism.git . + +# Set the working directory to the root of the monorepo +WORKDIR /app + +# Install correct foundry version +RUN just update-foundry + +# Set the working directory to the root of the contracts package +WORKDIR /app/packages/contracts-bedrock + +# Install dependencies +RUN forge install --shallow + +# Build the contracts package +RUN forge build + +# Deliberately run the upgrade script with invalid args to trigger a build +RUN forge script ./scripts/upgrades/holocene/DeployUpgrade.s.sol || true + +# Set the working directory to where upgrade.sh is located +WORKDIR /app/packages/contracts-bedrock/scripts/upgrades/holocene + +# allows to use modified local scripts +COPY scripts/*.sh ./scripts/ + +# Set the entrypoint to the main.sh script +ENTRYPOINT ["./scripts/main.sh"] diff --git a/packages/contracts-bedrock/semver-lock.json b/packages/contracts-bedrock/semver-lock.json index 1e14f5d286..2075985567 100644 --- a/packages/contracts-bedrock/semver-lock.json +++ b/packages/contracts-bedrock/semver-lock.json @@ -48,11 +48,11 @@ "sourceCodeHash": "0x39489a85bc3a5c8560f82d41b31bf7fe22f5b648f4ed538f61695a73092ea9eb" }, "src/L1/SystemConfig.sol": { - "initCodeHash": "0x429058f75d97fa7a7d0166b59830909bc722324feefc40f2b41419d6335d3f37", - "sourceCodeHash": "0x5ca776041a4ddc0d28ec55db7012d669481cd4601b0e71dbd3493a67b8a7e5a5" + "initCodeHash": "0x387aa0c2e404065420dcd3af7e0ebbb60d23569863e06e79d93b8f1561bab2a7", + "sourceCodeHash": "0x6db1dee07eb951e805dedd15335f0abc3f9bea1582eb1b0404939d2ffaf35010" }, "src/L1/SystemConfigInterop.sol": { - "initCodeHash": "0x277a61dcabed81a15739a8e9ed50615252bcc687cebea852e00191d0a1fbe11f", + "initCodeHash": "0x39b23a17137c2842c2c77edada51de99b48812969b3b5e41586e58e2a03c8e36", "sourceCodeHash": "0x38361a4f70a19e1b7819e933932a0c9fd2bcebaaebcbc7942f5c00dfaa2c28df" }, "src/L2/BaseFeeVault.sol": { @@ -140,8 +140,8 @@ "sourceCodeHash": "0x0fa0633a769e73f5937514c0003ba7947a1c275bbe5b85d78879c42f0ed8895b" }, "src/cannon/MIPS.sol": { - "initCodeHash": "0xa3cbf121bad13c00227ea4fef128853d9a86b7ec9158de894f99b58d38d7630a", - "sourceCodeHash": "0xd8467700c80b3e62fa37193dc6513bac35282094b686b50e162e157f704dde00" + "initCodeHash": "0xa1fa4df504af9e77aa973ff17fca2576b1bb7472b1390ea4251a4b7545bcf249", + "sourceCodeHash": "0x423558b9b3ed8d210f436f8101c80a5883143eee20760d1d399d642c192e1c40" }, "src/cannon/MIPS2.sol": { "initCodeHash": "0xaedf0d0b0e94a0c5e7d987331d2fdba84230f5704a6ca33677e70cde7051b17e", @@ -168,8 +168,8 @@ "sourceCodeHash": "0x9cb0851b6e471461f2bb369bd72eef4cffe8a0d1345546608a2aa6795540211d" }, "src/dispute/FaultDisputeGame.sol": { - "initCodeHash": "0x1480098a19f71ce6b4f4548880c8794402315ed3efa6793241a3df0fae864205", - "sourceCodeHash": "0x2f084f3f0e52017beb2ecf571178b94ba885cca987ada472d9e178b486a91d9e" + "initCodeHash": "0x63bb65e5ee6365d6ec1fa06469ba0fb415a2829b2b5dce2b13c4cbeb20606457", + "sourceCodeHash": "0x935757b8b8de69af854af8690cf56d24e2e94b1809b99a9bf0396bc6b5b6be20" }, "src/legacy/DeployerWhitelist.sol": { "initCodeHash": "0x0b8177ed75b69eddbb9ce6537683f69a9935efed86a1d6faa8feaafbd151c1bd", diff --git a/packages/contracts-bedrock/src/L1/SystemConfig.sol b/packages/contracts-bedrock/src/L1/SystemConfig.sol index afb9525403..90b57cbf47 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfig.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfig.sol @@ -137,9 +137,9 @@ contract SystemConfig is OwnableUpgradeable, ISemver, IGasToken { event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 2.3.0-beta.5 + /// @custom:semver 2.3.0 function version() public pure virtual returns (string memory) { - return "2.3.0-beta.5"; + return "2.3.0"; } /// @notice Constructs the SystemConfig contract. Cannot set diff --git a/packages/contracts-bedrock/src/cannon/MIPS.sol b/packages/contracts-bedrock/src/cannon/MIPS.sol index 5efab0ce98..347949f27e 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS.sol @@ -44,8 +44,8 @@ contract MIPS is ISemver { } /// @notice The semantic version of the MIPS contract. - /// @custom:semver 1.2.1-beta.7 - string public constant version = "1.2.1-beta.7"; + /// @custom:semver 1.2.1 + string public constant version = "1.2.1"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol b/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol index 9466465bd7..7f425370de 100644 --- a/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol +++ b/packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol @@ -147,8 +147,8 @@ contract FaultDisputeGame is Clone, ISemver { uint256 internal constant HEADER_BLOCK_NUMBER_INDEX = 8; /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.6 - string public constant version = "1.3.1-beta.6"; + /// @custom:semver 1.3.1 + string public constant version = "1.3.1"; /// @notice The starting timestamp of the game Timestamp public createdAt;