diff --git a/op-chain-ops/Makefile b/op-chain-ops/Makefile index 630167f7b60ef..262a989bc4658 100644 --- a/op-chain-ops/Makefile +++ b/op-chain-ops/Makefile @@ -44,4 +44,8 @@ fuzz: go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzAliasing ./crossdomain go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz=FuzzVersionedNonce ./crossdomain -.PHONY: test fuzz op-deployer \ No newline at end of file + +sync-standard-version: + curl -Lo ./deployer/opsm/standard-versions.toml https://raw.githubusercontent.com/ethereum-optimism/superchain-registry/refs/heads/main/validation/standard/standard-versions.toml + +.PHONY: test fuzz op-deployer sync-standard-version \ No newline at end of file diff --git a/op-chain-ops/deployer/integration_test/apply_test.go b/op-chain-ops/deployer/integration_test/apply_test.go index b69595b67761b..f3bcd5b3e4bd1 100644 --- a/op-chain-ops/deployer/integration_test/apply_test.go +++ b/op-chain-ops/deployer/integration_test/apply_test.go @@ -115,6 +115,7 @@ func TestEndToEndApply(t *testing.T) { UseFaultProofs: true, FundDevAccounts: true, ContractArtifactsURL: (*state.ArtifactsURL)(artifactsURL), + ContractsRelease: "dev", Chains: []*state.ChainIntent{ { ID: id.Bytes32(), diff --git a/op-chain-ops/deployer/opsm/implementations.go b/op-chain-ops/deployer/opsm/implementations.go index ed20b55f15229..d60330440abca 100644 --- a/op-chain-ops/deployer/opsm/implementations.go +++ b/op-chain-ops/deployer/opsm/implementations.go @@ -23,6 +23,7 @@ type DeployImplementationsInput struct { UseInterop bool // if true, deploy Interop implementations SuperchainProxyAdmin common.Address + StandardVersionsToml string // contents of 'standard-versions.toml' file } func (input *DeployImplementationsInput) InputSet() bool { @@ -31,6 +32,7 @@ func (input *DeployImplementationsInput) InputSet() bool { type DeployImplementationsOutput struct { OpsmProxy common.Address + OpsmImpl common.Address DelayedWETHImpl common.Address OptimismPortalImpl common.Address PreimageOracleSingleton common.Address diff --git a/op-chain-ops/deployer/opsm/standard-versions.toml b/op-chain-ops/deployer/opsm/standard-versions.toml new file mode 100644 index 0000000000000..cb4d336a73364 --- /dev/null +++ b/op-chain-ops/deployer/opsm/standard-versions.toml @@ -0,0 +1,47 @@ +standard_release = "op-contracts/v1.6.0" + +[releases] + +# Contracts which are +# * unproxied singletons: specify a standard "address" +# * proxied : specify a standard "implementation_address" +# * neither : specify neither a standard "address" nor "implementation_address" + +# Fault Proofs https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.6.0 +[releases."op-contracts/v1.6.0"] +optimism_portal = { version = "3.10.0", implementation_address = "0xe2F826324b2faf99E513D16D266c3F80aE87832B" } +system_config = { version = "2.2.0", implementation_address = "0xF56D96B2535B932656d3c04Ebf51baBff241D886" } +anchor_state_registry = { version = "2.0.0" } +delayed_weth = { version = "1.1.0", implementation_address = "0x71e966Ae981d1ce531a7b6d23DC0f27B38409087" } +dispute_game_factory = { version = "1.0.0", implementation_address = "0xc641A33cab81C559F2bd4b21EA34C290E2440C2B" } +fault_dispute_game = { version = "1.3.0" } +permissioned_dispute_game = { version = "1.3.0" } +mips = { version = "1.1.0", address = "0x16e83cE5Ce29BF90AD9Da06D2fE6a15d5f344ce4" } +preimage_oracle = { version = "1.1.2", address = "0x9c065e11870B891D214Bc2Da7EF1f9DDFA1BE277" } +l1_cross_domain_messenger = { version = "2.3.0", implementation_address = "0xD3494713A5cfaD3F5359379DfA074E2Ac8C6Fd65" } +l1_erc721_bridge = { version = "2.1.0", implementation_address = "0xAE2AF01232a6c4a4d3012C5eC5b1b35059caF10d" } +l1_standard_bridge = { version = "2.1.0", implementation_address = "0x64B5a5Ed26DCb17370Ff4d33a8D503f0fbD06CfF" } +# l2_output_oracle -- This contract not used in fault proofs +optimism_mintable_erc20_factory = { version = "1.9.0", implementation_address = "0xE01efbeb1089D1d1dB9c6c8b135C934C0734c846" } + +# Fault Proofs https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.4.0 +[releases."op-contracts/v1.4.0"] +optimism_portal = { version = "3.10.0", implementation_address = "0xe2F826324b2faf99E513D16D266c3F80aE87832B" } +system_config = { version = "2.2.0", implementation_address = "0xF56D96B2535B932656d3c04Ebf51baBff241D886" } +anchor_state_registry = { version = "1.0.0" } +delayed_weth = { version = "1.0.0", implementation_address = "0x97988d5624F1ba266E1da305117BCf20713bee08" } +dispute_game_factory = { version = "1.0.0", implementation_address = "0xc641A33cab81C559F2bd4b21EA34C290E2440C2B" } +fault_dispute_game = { version = "1.2.0" } +permissioned_dispute_game = { version = "1.2.0" } +mips = { version = "1.0.1", address = "0x0f8EdFbDdD3c0256A80AD8C0F2560B1807873C9c" } +preimage_oracle = { version = "1.0.0", address = "0xD326E10B8186e90F4E2adc5c13a2d0C137ee8b34" } + +# MCP https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.3.0 +[releases."op-contracts/v1.3.0"] +l1_cross_domain_messenger = { version = "2.3.0", implementation_address = "0xD3494713A5cfaD3F5359379DfA074E2Ac8C6Fd65" } +l1_erc721_bridge = { version = "2.1.0", implementation_address = "0xAE2AF01232a6c4a4d3012C5eC5b1b35059caF10d" } +l1_standard_bridge = { version = "2.1.0", implementation_address = "0x64B5a5Ed26DCb17370Ff4d33a8D503f0fbD06CfF" } +l2_output_oracle = { version = "1.8.0", implementation_address = "0xF243BEd163251380e78068d317ae10f26042B292" } +optimism_mintable_erc20_factory = { version = "1.9.0", implementation_address = "0xE01efbeb1089D1d1dB9c6c8b135C934C0734c846" } +optimism_portal = { version = "2.5.0", implementation_address = "0x2D778797049FE9259d947D1ED8e5442226dFB589" } +system_config = { version = "1.12.0", implementation_address = "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1" } diff --git a/op-chain-ops/deployer/opsm/standard.go b/op-chain-ops/deployer/opsm/standard.go new file mode 100644 index 0000000000000..56f0d7ada37b7 --- /dev/null +++ b/op-chain-ops/deployer/opsm/standard.go @@ -0,0 +1,8 @@ +package opsm + +import "embed" + +//go:embed standard-versions.toml +var StandardVersionsData string + +var _ embed.FS diff --git a/op-chain-ops/deployer/pipeline/implementations.go b/op-chain-ops/deployer/pipeline/implementations.go index f9e125e4150bc..0dcda8feea192 100644 --- a/op-chain-ops/deployer/pipeline/implementations.go +++ b/op-chain-ops/deployer/pipeline/implementations.go @@ -46,10 +46,11 @@ func DeployImplementations(ctx context.Context, env *Env, artifactsFS foundry.St ChallengePeriodSeconds: big.NewInt(86400), ProofMaturityDelaySeconds: big.NewInt(604800), DisputeGameFinalityDelaySeconds: big.NewInt(302400), - Release: "op-contracts/v1.6.0", + Release: intent.ContractsRelease, SuperchainConfigProxy: st.SuperchainDeployment.SuperchainConfigProxyAddress, ProtocolVersionsProxy: st.SuperchainDeployment.ProtocolVersionsProxyAddress, SuperchainProxyAdmin: st.SuperchainDeployment.ProxyAdminAddress, + StandardVersionsToml: opsm.StandardVersionsData, UseInterop: false, }, ) diff --git a/op-chain-ops/deployer/state/intent.go b/op-chain-ops/deployer/state/intent.go index c737dab37dd0c..17bedacd77b5d 100644 --- a/op-chain-ops/deployer/state/intent.go +++ b/op-chain-ops/deployer/state/intent.go @@ -24,6 +24,8 @@ type Intent struct { ContractArtifactsURL *ArtifactsURL `json:"contractArtifactsURL" toml:"contractArtifactsURL"` + ContractsRelease string `json:"contractsVersion" toml:"contractsVersion"` + Chains []*ChainIntent `json:"chains" toml:"chains"` GlobalDeployOverrides map[string]any `json:"globalDeployOverrides" toml:"globalDeployOverrides"` diff --git a/op-chain-ops/interopgen/configs.go b/op-chain-ops/interopgen/configs.go index 0bc939ec0351e..9abe9880fe8be 100644 --- a/op-chain-ops/interopgen/configs.go +++ b/op-chain-ops/interopgen/configs.go @@ -39,6 +39,8 @@ type OPSMImplementationsConfig struct { FaultProof SuperFaultProofConfig UseInterop bool // to deploy Interop implementation contracts, instead of the regular ones. + + StandardVersionsToml string // serialized string of superchain-registry 'standard-versions.toml' file } type SuperchainConfig struct { diff --git a/op-chain-ops/interopgen/deploy.go b/op-chain-ops/interopgen/deploy.go index fde7485e04da4..9da41e9894d83 100644 --- a/op-chain-ops/interopgen/deploy.go +++ b/op-chain-ops/interopgen/deploy.go @@ -172,6 +172,7 @@ func deploySuperchainToL1(l1Host *script.Host, superCfg *SuperchainConfig) (*Sup ProtocolVersionsProxy: superDeployment.ProtocolVersionsProxy, SuperchainProxyAdmin: superDeployment.SuperchainProxyAdmin, UseInterop: superCfg.Implementations.UseInterop, + StandardVersionsToml: opsm.StandardVersionsData, }) if err != nil { return nil, fmt.Errorf("failed to deploy Implementations contracts: %w", err) diff --git a/op-chain-ops/interopgen/deployments.go b/op-chain-ops/interopgen/deployments.go index 5b54c2286f9a1..b6bb124d8e85d 100644 --- a/op-chain-ops/interopgen/deployments.go +++ b/op-chain-ops/interopgen/deployments.go @@ -10,6 +10,7 @@ type L1Deployment struct { type Implementations struct { OpsmProxy common.Address `json:"OPSMProxy"` + OpsmImpl common.Address `json:"OPSMImpl"` DelayedWETHImpl common.Address `json:"DelayedWETHImpl"` OptimismPortalImpl common.Address `json:"OptimismPortalImpl"` PreimageOracleSingleton common.Address `json:"PreimageOracleSingleton"` diff --git a/op-chain-ops/interopgen/recipe.go b/op-chain-ops/interopgen/recipe.go index a1761f9f0dc8c..4dbe58e4ca1f0 100644 --- a/op-chain-ops/interopgen/recipe.go +++ b/op-chain-ops/interopgen/recipe.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum-optimism/optimism/op-chain-ops/deployer/opsm" "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" ) @@ -61,12 +62,13 @@ func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error) l1Cfg.Prefund[superchainDeployer] = Ether(10_000_000) l1Cfg.Prefund[superchainProxyAdmin] = Ether(10_000_000) l1Cfg.Prefund[superchainConfigGuardian] = Ether(10_000_000) + superchainCfg := &SuperchainConfig{ ProxyAdminOwner: superchainProxyAdmin, ProtocolVersionsOwner: superchainProtocolVersionsOwner, Deployer: superchainDeployer, Implementations: OPSMImplementationsConfig{ - Release: "op-contracts/0.0.1", + Release: "dev", FaultProof: SuperFaultProofConfig{ WithdrawalDelaySeconds: big.NewInt(604800), MinProposalSizeBytes: big.NewInt(10000), @@ -74,7 +76,8 @@ func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error) ProofMaturityDelaySeconds: big.NewInt(12), DisputeGameFinalityDelaySeconds: big.NewInt(6), }, - UseInterop: true, + UseInterop: true, + StandardVersionsToml: opsm.StandardVersionsData, }, SuperchainL1DeployConfig: genesis.SuperchainL1DeployConfig{ RequiredProtocolVersion: params.OPStackSupport, diff --git a/op-chain-ops/script/cheatcodes_utilities.go b/op-chain-ops/script/cheatcodes_utilities.go index 2ae53a52e9109..022befa606275 100644 --- a/op-chain-ops/script/cheatcodes_utilities.go +++ b/op-chain-ops/script/cheatcodes_utilities.go @@ -3,9 +3,12 @@ package script import ( "fmt" "math/big" + "regexp" "strconv" "strings" + "github.com/BurntSushi/toml" + hdwallet "github.com/ethereum-optimism/go-ethereum-hdwallet" "github.com/ethereum/go-ethereum/accounts" @@ -188,5 +191,77 @@ func (c *CheatCodesPrecompile) Breakpoint_f7d39a8d(name string, v bool) { } } +// ParseTomlAddress_65e7c844 implements https://book.getfoundry.sh/cheatcodes/parse-toml. This +// method is not well optimized or implemented. It's optimized for quickly delivering OPCM. We +// can come back and clean it up more later. +func (c *CheatCodesPrecompile) ParseTomlAddress_65e7c844(tomlStr string, key string) (common.Address, error) { + var data map[string]any + if err := toml.Unmarshal([]byte(tomlStr), &data); err != nil { + return common.Address{}, fmt.Errorf("failed to parse TOML: %w", err) + } + + keys, err := SplitJSONPathKeys(key) + if err != nil { + return common.Address{}, fmt.Errorf("failed to split keys: %w", err) + } + + loc := data + for i, k := range keys { + value, ok := loc[k] + if !ok { + return common.Address{}, fmt.Errorf("key %q not found in TOML", k) + } + + if i == len(keys)-1 { + addrStr, ok := value.(string) + if !ok { + return common.Address{}, fmt.Errorf("key %q is not a string", key) + } + if !common.IsHexAddress(addrStr) { + return common.Address{}, fmt.Errorf("key %q is not a valid address", key) + } + return common.HexToAddress(addrStr), nil + } + + next, ok := value.(map[string]any) + if !ok { + return common.Address{}, fmt.Errorf("key %q is not a nested map", key) + } + loc = next + } + + panic("should never get here") +} + // unsupported //func (c *CheatCodesPrecompile) CreateWallet() {} + +// SplitJSONPathKeys splits a JSON path into keys. It supports bracket notation. There is a much +// better way to implement this, but I'm keeping this simple for now. +func SplitJSONPathKeys(path string) ([]string, error) { + var out []string + bracketSplit := regexp.MustCompile(`[\[\]]`).Split(path, -1) + for _, split := range bracketSplit { + if len(split) == 0 { + continue + } + + split = strings.ReplaceAll(split, "\"", "") + split = strings.ReplaceAll(split, " ", "") + + if !strings.HasPrefix(split, ".") { + out = append(out, split) + continue + } + + keys := strings.Split(split, ".") + for _, key := range keys { + if len(key) == 0 { + continue + } + out = append(out, key) + } + } + + return out, nil +} diff --git a/op-chain-ops/script/cheatcodes_utilities_test.go b/op-chain-ops/script/cheatcodes_utilities_test.go new file mode 100644 index 0000000000000..23936a10e344e --- /dev/null +++ b/op-chain-ops/script/cheatcodes_utilities_test.go @@ -0,0 +1,59 @@ +package script + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +const tomlTest = ` +foo = "0x0d4CE7B6a91A35c31D7D62b327D19617c8da6F23" + +[foomap] +[foomap."bar.bump"] +baz = "0xff4ce7b6a91a35c31d7d62b327d19617c8da6f23" +` + +func TestSplitJSONPathKeys(t *testing.T) { + tests := []struct { + name string + path string + expected []string + }{ + { + "simple", + ".foo.bar", + []string{"foo", "bar"}, + }, + { + "bracket keys", + ".foo[\"hey\"].bar", + []string{"foo", "hey", "bar"}, + }, + { + "bracket keys with dots", + ".foo[\"hey.there\"].bar", + []string{"foo", "hey.there", "bar"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := SplitJSONPathKeys(tt.path) + require.NoError(t, err) + require.Equal(t, tt.expected, got) + }) + } +} + +func TestParseTomlAddress(t *testing.T) { + c := &CheatCodesPrecompile{} + + addr, err := c.ParseTomlAddress_65e7c844(tomlTest, "foo") + require.NoError(t, err) + require.Equal(t, common.HexToAddress("0x0d4ce7b6a91a35c31d7d62b327d19617c8da6f23"), addr) + + addr, err = c.ParseTomlAddress_65e7c844(tomlTest, "foomap[\"bar.bump\"].baz") + require.NoError(t, err) + require.Equal(t, common.HexToAddress("0xff4ce7b6a91a35c31d7d62b327d19617c8da6f23"), addr) +} diff --git a/packages/contracts-bedrock/.testdata/.gitkeep b/packages/contracts-bedrock/.testdata/.gitkeep deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/packages/contracts-bedrock/scripts/DeployImplementations.s.sol b/packages/contracts-bedrock/scripts/DeployImplementations.s.sol index 81cafa89c272c..df950fed71f27 100644 --- a/packages/contracts-bedrock/scripts/DeployImplementations.s.sol +++ b/packages/contracts-bedrock/scripts/DeployImplementations.s.sol @@ -62,6 +62,8 @@ contract DeployImplementationsInput is BaseDeployIO { SuperchainConfig internal _superchainConfigProxy; ProtocolVersions internal _protocolVersionsProxy; + string internal _standardVersionsToml; + function set(bytes4 sel, uint256 _value) public { require(_value != 0, "DeployImplementationsInput: cannot set zero value"); @@ -84,6 +86,7 @@ contract DeployImplementationsInput is BaseDeployIO { function set(bytes4 sel, string memory _value) public { require(!LibString.eq(_value, ""), "DeployImplementationsInput: cannot set empty string"); if (sel == this.release.selector) _release = _value; + else if (sel == this.standardVersionsToml.selector) _standardVersionsToml = _value; else revert("DeployImplementationsInput: unknown selector"); } @@ -137,6 +140,11 @@ contract DeployImplementationsInput is BaseDeployIO { return _release; } + function standardVersionsToml() public view returns (string memory) { + require(!LibString.eq(_standardVersionsToml, ""), "DeployImplementationsInput: not set"); + return _standardVersionsToml; + } + function superchainConfigProxy() public view returns (SuperchainConfig) { require(address(_superchainConfigProxy) != address(0), "DeployImplementationsInput: not set"); return _superchainConfigProxy; @@ -159,6 +167,7 @@ contract DeployImplementationsInput is BaseDeployIO { contract DeployImplementationsOutput is BaseDeployIO { OPStackManager internal _opsmProxy; + OPStackManager internal _opsmImpl; DelayedWETH internal _delayedWETHImpl; OptimismPortal2 internal _optimismPortalImpl; PreimageOracle internal _preimageOracleSingleton; @@ -175,6 +184,7 @@ contract DeployImplementationsOutput is BaseDeployIO { // forgefmt: disable-start if (sel == this.opsmProxy.selector) _opsmProxy = OPStackManager(payable(_addr)); + else if (sel == this.opsmImpl.selector) _opsmImpl = OPStackManager(payable(_addr)); else if (sel == this.optimismPortalImpl.selector) _optimismPortalImpl = OptimismPortal2(payable(_addr)); else if (sel == this.delayedWETHImpl.selector) _delayedWETHImpl = DelayedWETH(payable(_addr)); else if (sel == this.preimageOracleSingleton.selector) _preimageOracleSingleton = PreimageOracle(_addr); @@ -190,12 +200,18 @@ contract DeployImplementationsOutput is BaseDeployIO { } function checkOutput(DeployImplementationsInput _dii) public { - address[] memory addrs = Solarray.addresses( + // With 12 addresses, we'd get a stack too deep error if we tried to do this inline as a + // single call to `Solarray.addresses`. So we split it into two calls. + address[] memory addrs1 = Solarray.addresses( address(this.opsmProxy()), + address(this.opsmImpl()), address(this.optimismPortalImpl()), address(this.delayedWETHImpl()), address(this.preimageOracleSingleton()), - address(this.mipsSingleton()), + address(this.mipsSingleton()) + ); + + address[] memory addrs2 = Solarray.addresses( address(this.systemConfigImpl()), address(this.l1CrossDomainMessengerImpl()), address(this.l1ERC721BridgeImpl()), @@ -203,7 +219,8 @@ contract DeployImplementationsOutput is BaseDeployIO { address(this.optimismMintableERC20FactoryImpl()), address(this.disputeGameFactoryImpl()) ); - DeployUtils.assertValidContractAddresses(addrs); + + DeployUtils.assertValidContractAddresses(Solarray.extend(addrs1, addrs2)); assertValidDeploy(_dii); } @@ -214,6 +231,11 @@ contract DeployImplementationsOutput is BaseDeployIO { return _opsmProxy; } + function opsmImpl() public view returns (OPStackManager) { + DeployUtils.assertValidContractAddress(address(_opsmImpl)); + return _opsmImpl; + } + function optimismPortalImpl() public view returns (OptimismPortal2) { DeployUtils.assertValidContractAddress(address(_optimismPortalImpl)); return _optimismPortalImpl; @@ -474,26 +496,30 @@ contract DeployImplementations is Script { // Deploy and initialize a proxied OPStackManager. function createOPSMContract( DeployImplementationsInput _dii, - DeployImplementationsOutput, - OPStackManager.Blueprints memory blueprints, - string memory release, - OPStackManager.ImplementationSetter[] memory setters + DeployImplementationsOutput _dio, + OPStackManager.Blueprints memory _blueprints, + string memory _release, + OPStackManager.ImplementationSetter[] memory _setters ) internal virtual returns (OPStackManager opsmProxy_) { - SuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); - ProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); ProxyAdmin proxyAdmin = _dii.superchainProxyAdmin(); - vm.startBroadcast(msg.sender); + vm.broadcast(msg.sender); Proxy proxy = new Proxy(address(msg.sender)); - OPStackManager opsm = new OPStackManager(superchainConfigProxy, protocolVersionsProxy); + + deployOPContractsManagerImpl(_dii, _dio); + OPStackManager opsmImpl = _dio.opsmImpl(); OPStackManager.InitializerInputs memory initializerInputs = - OPStackManager.InitializerInputs(blueprints, setters, release, true); - proxy.upgradeToAndCall(address(opsm), abi.encodeWithSelector(opsm.initialize.selector, initializerInputs)); + OPStackManager.InitializerInputs(_blueprints, _setters, _release, true); + + vm.startBroadcast(msg.sender); + proxy.upgradeToAndCall( + address(opsmImpl), abi.encodeWithSelector(opsmImpl.initialize.selector, initializerInputs) + ); proxy.changeAdmin(address(proxyAdmin)); // transfer ownership of Proxy contract to the ProxyAdmin contract vm.stopBroadcast(); @@ -572,56 +598,148 @@ contract DeployImplementations is Script { // --- Core Contracts --- - function deploySystemConfigImpl(DeployImplementationsInput, DeployImplementationsOutput _dio) public virtual { - vm.broadcast(msg.sender); - SystemConfig systemConfigImpl = new SystemConfig(); + function deploySystemConfigImpl(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public virtual { + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + // Using snake case for contract name to match the TOML file in superchain-registry. + string memory contractName = "system_config"; + SystemConfig impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = SystemConfig(existingImplementation); + } else if (isDevelopRelease(release)) { + // Deploy a new implementation for development builds. + vm.broadcast(msg.sender); + impl = new SystemConfig(); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(systemConfigImpl), "SystemConfigImpl"); - _dio.set(_dio.systemConfigImpl.selector, address(systemConfigImpl)); + vm.label(address(impl), "SystemConfigImpl"); + _dio.set(_dio.systemConfigImpl.selector, address(impl)); } function deployL1CrossDomainMessengerImpl( - DeployImplementationsInput, + DeployImplementationsInput _dii, DeployImplementationsOutput _dio ) public virtual { - vm.broadcast(msg.sender); - L1CrossDomainMessenger l1CrossDomainMessengerImpl = new L1CrossDomainMessenger(); + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "l1_cross_domain_messenger"; + L1CrossDomainMessenger impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = L1CrossDomainMessenger(existingImplementation); + } else if (isDevelopRelease(release)) { + vm.broadcast(msg.sender); + impl = new L1CrossDomainMessenger(); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(l1CrossDomainMessengerImpl), "L1CrossDomainMessengerImpl"); - _dio.set(_dio.l1CrossDomainMessengerImpl.selector, address(l1CrossDomainMessengerImpl)); + vm.label(address(impl), "L1CrossDomainMessengerImpl"); + _dio.set(_dio.l1CrossDomainMessengerImpl.selector, address(impl)); } - function deployL1ERC721BridgeImpl(DeployImplementationsInput, DeployImplementationsOutput _dio) public virtual { - vm.broadcast(msg.sender); - L1ERC721Bridge l1ERC721BridgeImpl = new L1ERC721Bridge(); + function deployL1ERC721BridgeImpl( + DeployImplementationsInput _dii, + DeployImplementationsOutput _dio + ) + public + virtual + { + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "l1_erc721_bridge"; + L1ERC721Bridge impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = L1ERC721Bridge(existingImplementation); + } else if (isDevelopRelease(release)) { + vm.broadcast(msg.sender); + impl = new L1ERC721Bridge(); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(l1ERC721BridgeImpl), "L1ERC721BridgeImpl"); - _dio.set(_dio.l1ERC721BridgeImpl.selector, address(l1ERC721BridgeImpl)); + vm.label(address(impl), "L1ERC721BridgeImpl"); + _dio.set(_dio.l1ERC721BridgeImpl.selector, address(impl)); } - function deployL1StandardBridgeImpl(DeployImplementationsInput, DeployImplementationsOutput _dio) public virtual { - vm.broadcast(msg.sender); - L1StandardBridge l1StandardBridgeImpl = new L1StandardBridge(); + function deployL1StandardBridgeImpl( + DeployImplementationsInput _dii, + DeployImplementationsOutput _dio + ) + public + virtual + { + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "l1_standard_bridge"; + L1StandardBridge impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = L1StandardBridge(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + vm.broadcast(msg.sender); + impl = new L1StandardBridge(); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(l1StandardBridgeImpl), "L1StandardBridgeImpl"); - _dio.set(_dio.l1StandardBridgeImpl.selector, address(l1StandardBridgeImpl)); + vm.label(address(impl), "L1StandardBridgeImpl"); + _dio.set(_dio.l1StandardBridgeImpl.selector, address(impl)); } function deployOptimismMintableERC20FactoryImpl( - DeployImplementationsInput, + DeployImplementationsInput _dii, DeployImplementationsOutput _dio ) public virtual { + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "optimism_mintable_erc20_factory"; + OptimismMintableERC20Factory impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = OptimismMintableERC20Factory(existingImplementation); + } else if (isDevelopRelease(release)) { + vm.broadcast(msg.sender); + impl = new OptimismMintableERC20Factory(); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } + + vm.label(address(impl), "OptimismMintableERC20FactoryImpl"); + _dio.set(_dio.optimismMintableERC20FactoryImpl.selector, address(impl)); + } + + function deployOPContractsManagerImpl( + DeployImplementationsInput _dii, + DeployImplementationsOutput _dio + ) + public + virtual + { + SuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); + ProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); + vm.broadcast(msg.sender); - OptimismMintableERC20Factory optimismMintableERC20FactoryImpl = new OptimismMintableERC20Factory(); + // TODO: Eventually we will want to select the correct implementation based on the release. + OPStackManager impl = new OPStackManager(superchainConfigProxy, protocolVersionsProxy); - vm.label(address(optimismMintableERC20FactoryImpl), "OptimismMintableERC20FactoryImpl"); - _dio.set(_dio.optimismMintableERC20FactoryImpl.selector, address(optimismMintableERC20FactoryImpl)); + vm.label(address(impl), "OPStackManagerImpl"); + _dio.set(_dio.opsmImpl.selector, address(impl)); } // --- Fault Proofs Contracts --- @@ -659,27 +777,46 @@ contract DeployImplementations is Script { public virtual { - uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); - uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); - - vm.broadcast(msg.sender); - OptimismPortal2 optimismPortalImpl = new OptimismPortal2({ - _proofMaturityDelaySeconds: proofMaturityDelaySeconds, - _disputeGameFinalityDelaySeconds: disputeGameFinalityDelaySeconds - }); + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "optimism_portal"; + OptimismPortal2 impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = OptimismPortal2(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); + uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); + vm.broadcast(msg.sender); + impl = new OptimismPortal2(proofMaturityDelaySeconds, disputeGameFinalityDelaySeconds); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(optimismPortalImpl), "OptimismPortalImpl"); - _dio.set(_dio.optimismPortalImpl.selector, address(optimismPortalImpl)); + vm.label(address(impl), "OptimismPortalImpl"); + _dio.set(_dio.optimismPortalImpl.selector, address(impl)); } function deployDelayedWETHImpl(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public virtual { - uint256 withdrawalDelaySeconds = _dii.withdrawalDelaySeconds(); - - vm.broadcast(msg.sender); - DelayedWETH delayedWETHImpl = new DelayedWETH({ _delay: withdrawalDelaySeconds }); + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "delayed_weth"; + DelayedWETH impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = DelayedWETH(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + uint256 withdrawalDelaySeconds = _dii.withdrawalDelaySeconds(); + vm.broadcast(msg.sender); + impl = new DelayedWETH(withdrawalDelaySeconds); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(delayedWETHImpl), "DelayedWETHImpl"); - _dio.set(_dio.delayedWETHImpl.selector, address(delayedWETHImpl)); + vm.label(address(impl), "DelayedWETHImpl"); + _dio.set(_dio.delayedWETHImpl.selector, address(impl)); } function deployPreimageOracleSingleton( @@ -689,39 +826,72 @@ contract DeployImplementations is Script { public virtual { - uint256 minProposalSizeBytes = _dii.minProposalSizeBytes(); - uint256 challengePeriodSeconds = _dii.challengePeriodSeconds(); - - vm.broadcast(msg.sender); - PreimageOracle preimageOracleSingleton = - new PreimageOracle({ _minProposalSize: minProposalSizeBytes, _challengePeriod: challengePeriodSeconds }); + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "preimage_oracle"; + PreimageOracle singleton; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + singleton = PreimageOracle(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + uint256 minProposalSizeBytes = _dii.minProposalSizeBytes(); + uint256 challengePeriodSeconds = _dii.challengePeriodSeconds(); + vm.broadcast(msg.sender); + singleton = new PreimageOracle(minProposalSizeBytes, challengePeriodSeconds); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(preimageOracleSingleton), "PreimageOracleSingleton"); - _dio.set(_dio.preimageOracleSingleton.selector, address(preimageOracleSingleton)); + vm.label(address(singleton), "PreimageOracleSingleton"); + _dio.set(_dio.preimageOracleSingleton.selector, address(singleton)); } - function deployMipsSingleton(DeployImplementationsInput, DeployImplementationsOutput _dio) public virtual { - IPreimageOracle preimageOracle = IPreimageOracle(_dio.preimageOracleSingleton()); - - vm.broadcast(msg.sender); - MIPS mipsSingleton = new MIPS(preimageOracle); + function deployMipsSingleton(DeployImplementationsInput _dii, DeployImplementationsOutput _dio) public virtual { + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "mips"; + MIPS singleton; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + singleton = MIPS(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + IPreimageOracle preimageOracle = IPreimageOracle(_dio.preimageOracleSingleton()); + vm.broadcast(msg.sender); + singleton = new MIPS(preimageOracle); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(mipsSingleton), "MIPSSingleton"); - _dio.set(_dio.mipsSingleton.selector, address(mipsSingleton)); + vm.label(address(singleton), "MIPSSingleton"); + _dio.set(_dio.mipsSingleton.selector, address(singleton)); } function deployDisputeGameFactoryImpl( - DeployImplementationsInput, + DeployImplementationsInput _dii, DeployImplementationsOutput _dio ) public virtual { - vm.broadcast(msg.sender); - DisputeGameFactory disputeGameFactoryImpl = new DisputeGameFactory(); + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "dispute_game_factory"; + DisputeGameFactory impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = DisputeGameFactory(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + vm.broadcast(msg.sender); + impl = new DisputeGameFactory(); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.label(address(disputeGameFactoryImpl), "DisputeGameFactoryImpl"); - _dio.set(_dio.disputeGameFactoryImpl.selector, address(disputeGameFactoryImpl)); + vm.label(address(impl), "DisputeGameFactoryImpl"); + _dio.set(_dio.disputeGameFactoryImpl.selector, address(impl)); } // -------- Utilities -------- @@ -763,6 +933,35 @@ contract DeployImplementations is Script { newContract1_ = deployBytecode(part1, _salt); newContract2_ = deployBytecode(part2, _salt); } + + // Zero address is returned if the address is not found in '_standardVersionsToml'. + function getReleaseAddress( + string memory _version, + string memory _contractName, + string memory _standardVersionsToml + ) + internal + pure + returns (address addr_) + { + string memory baseKey = string.concat('.releases["', _version, '"].', _contractName); + string memory implAddressKey = string.concat(baseKey, ".implementation_address"); + string memory addressKey = string.concat(baseKey, ".address"); + try vm.parseTomlAddress(_standardVersionsToml, implAddressKey) returns (address parsedAddr_) { + addr_ = parsedAddr_; + } catch { + try vm.parseTomlAddress(_standardVersionsToml, addressKey) returns (address parsedAddr_) { + addr_ = parsedAddr_; + } catch { + addr_ = address(0); + } + } + } + + // A release is considered a 'develop' release if it does not start with 'op-contracts'. + function isDevelopRelease(string memory _release) internal pure returns (bool) { + return !LibString.startsWith(_release, "op-contracts"); + } } // Similar to how DeploySuperchain.s.sol contains a lot of comments to thoroughly document the script @@ -800,26 +999,30 @@ contract DeployImplementations is Script { contract DeployImplementationsInterop is DeployImplementations { function createOPSMContract( DeployImplementationsInput _dii, - DeployImplementationsOutput, - OPStackManager.Blueprints memory blueprints, - string memory release, - OPStackManager.ImplementationSetter[] memory setters + DeployImplementationsOutput _dio, + OPStackManager.Blueprints memory _blueprints, + string memory _release, + OPStackManager.ImplementationSetter[] memory _setters ) internal override returns (OPStackManager opsmProxy_) { - SuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); - ProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); ProxyAdmin proxyAdmin = _dii.superchainProxyAdmin(); - vm.startBroadcast(msg.sender); + vm.broadcast(msg.sender); Proxy proxy = new Proxy(address(msg.sender)); - OPStackManager opsm = new OPStackManagerInterop(superchainConfigProxy, protocolVersionsProxy); + + deployOPContractsManagerImpl(_dii, _dio); // overriding function + OPStackManager opsmImpl = _dio.opsmImpl(); OPStackManager.InitializerInputs memory initializerInputs = - OPStackManager.InitializerInputs(blueprints, setters, release, true); - proxy.upgradeToAndCall(address(opsm), abi.encodeWithSelector(opsm.initialize.selector, initializerInputs)); + OPStackManager.InitializerInputs(_blueprints, _setters, _release, true); + + vm.startBroadcast(msg.sender); + proxy.upgradeToAndCall( + address(opsmImpl), abi.encodeWithSelector(opsmImpl.initialize.selector, initializerInputs) + ); proxy.changeAdmin(address(proxyAdmin)); // transfer ownership of Proxy contract to the ProxyAdmin contract vm.stopBroadcast(); @@ -834,25 +1037,70 @@ contract DeployImplementationsInterop is DeployImplementations { public override { - uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); - uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + string memory contractName = "optimism_portal"; + OptimismPortal2 impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = OptimismPortalInterop(payable(existingImplementation)); + } else if (isDevelopRelease(release)) { + uint256 proofMaturityDelaySeconds = _dii.proofMaturityDelaySeconds(); + uint256 disputeGameFinalityDelaySeconds = _dii.disputeGameFinalityDelaySeconds(); + vm.broadcast(msg.sender); + impl = new OptimismPortalInterop(proofMaturityDelaySeconds, disputeGameFinalityDelaySeconds); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } - vm.broadcast(msg.sender); - OptimismPortalInterop optimismPortalImpl = new OptimismPortalInterop({ - _proofMaturityDelaySeconds: proofMaturityDelaySeconds, - _disputeGameFinalityDelaySeconds: disputeGameFinalityDelaySeconds - }); + vm.label(address(impl), "OptimismPortalImpl"); + _dio.set(_dio.optimismPortalImpl.selector, address(impl)); + } - vm.label(address(optimismPortalImpl), "OptimismPortalImpl"); - _dio.set(_dio.optimismPortalImpl.selector, address(optimismPortalImpl)); + function deploySystemConfigImpl( + DeployImplementationsInput _dii, + DeployImplementationsOutput _dio + ) + public + override + { + string memory release = _dii.release(); + string memory stdVerToml = _dii.standardVersionsToml(); + + string memory contractName = "system_config"; + SystemConfig impl; + + address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); + if (existingImplementation != address(0)) { + impl = SystemConfigInterop(existingImplementation); + } else if (isDevelopRelease(release)) { + vm.broadcast(msg.sender); + impl = new SystemConfigInterop(); + } else { + revert(string.concat("DeployImplementations: failed to deploy release ", release)); + } + + vm.label(address(impl), "SystemConfigImpl"); + _dio.set(_dio.systemConfigImpl.selector, address(impl)); } - function deploySystemConfigImpl(DeployImplementationsInput, DeployImplementationsOutput _dio) public override { + function deployOPContractsManagerImpl( + DeployImplementationsInput _dii, + DeployImplementationsOutput _dio + ) + public + override + { + SuperchainConfig superchainConfigProxy = _dii.superchainConfigProxy(); + ProtocolVersions protocolVersionsProxy = _dii.protocolVersionsProxy(); + vm.broadcast(msg.sender); - SystemConfigInterop systemConfigImpl = new SystemConfigInterop(); + // TODO: Eventually we will want to select the correct implementation based on the release. + OPStackManager impl = new OPStackManagerInterop(superchainConfigProxy, protocolVersionsProxy); - vm.label(address(systemConfigImpl), "SystemConfigImpl"); - _dio.set(_dio.systemConfigImpl.selector, address(systemConfigImpl)); + vm.label(address(impl), "OPStackManagerImpl"); + _dio.set(_dio.opsmImpl.selector, address(impl)); } function opsmSystemConfigSetter( diff --git a/packages/contracts-bedrock/test/DeployImplementations.t.sol b/packages/contracts-bedrock/test/DeployImplementations.t.sol index 957eb28304356..1eac67c6fdf31 100644 --- a/packages/contracts-bedrock/test/DeployImplementations.t.sol +++ b/packages/contracts-bedrock/test/DeployImplementations.t.sol @@ -35,7 +35,7 @@ contract DeployImplementationsInput_Test is Test { uint256 challengePeriodSeconds = 300; uint256 proofMaturityDelaySeconds = 400; uint256 disputeGameFinalityDelaySeconds = 500; - string release = "op-contracts/latest"; + string release = "dev-release"; // this means implementation contracts will be deployed SuperchainConfig superchainConfigProxy = SuperchainConfig(makeAddr("superchainConfigProxy")); ProtocolVersions protocolVersionsProxy = ProtocolVersions(makeAddr("protocolVersionsProxy")); @@ -70,6 +70,9 @@ contract DeployImplementationsInput_Test is Test { vm.expectRevert("DeployImplementationsInput: not set"); dii.superchainProxyAdmin(); + + vm.expectRevert("DeployImplementationsInput: not set"); + dii.standardVersionsToml(); } function test_superchainProxyAdmin_whenNotSet_reverts() public { @@ -247,13 +250,18 @@ contract DeployImplementations_Test is Test { uint256 challengePeriodSeconds = 300; uint256 proofMaturityDelaySeconds = 400; uint256 disputeGameFinalityDelaySeconds = 500; - string release = "op-contracts/latest"; SuperchainConfig superchainConfigProxy = SuperchainConfig(makeAddr("superchainConfigProxy")); ProtocolVersions protocolVersionsProxy = ProtocolVersions(makeAddr("protocolVersionsProxy")); function setUp() public virtual { deployImplementations = new DeployImplementations(); (dii, dio) = deployImplementations.etchIOContracts(); + + // End users of the DeployImplementations contract will need to set the `standardVersionsToml`. + string memory standardVersionsTomlPath = + string.concat(vm.projectRoot(), "/test/fixtures/standard-versions.toml"); + string memory standardVersionsToml = vm.readFile(standardVersionsTomlPath); + dii.set(dii.standardVersionsToml.selector, standardVersionsToml); } // By deploying the `DeployImplementations` contract with this virtual function, we provide a @@ -267,13 +275,142 @@ contract DeployImplementations_Test is Test { return keccak256(abi.encode(_seed, _i)); } + function test_deployImplementation_succeeds() public { + string memory deployContractsRelease = "dev-release"; + dii.set(dii.release.selector, deployContractsRelease); + deployImplementations.deploySystemConfigImpl(dii, dio); + assertTrue(address(0) != address(dio.systemConfigImpl())); + } + + function test_reuseImplementation_succeeds() public { + // All hardcoded addresses below are taken from the superchain-registry config: + // https://github.com/ethereum-optimism/superchain-registry/blob/be65d22f8128cf0c4e5b4e1f677daf86843426bf/validation/standard/standard-versions.toml#L11 + string memory testRelease = "op-contracts/v1.6.0"; + dii.set(dii.release.selector, testRelease); + + deployImplementations.deploySystemConfigImpl(dii, dio); + address srSystemConfigImpl = address(0xF56D96B2535B932656d3c04Ebf51baBff241D886); + vm.etch(address(srSystemConfigImpl), hex"01"); + assertEq(srSystemConfigImpl, address(dio.systemConfigImpl())); + + address srL1CrossDomainMessengerImpl = address(0xD3494713A5cfaD3F5359379DfA074E2Ac8C6Fd65); + vm.etch(address(srL1CrossDomainMessengerImpl), hex"01"); + deployImplementations.deployL1CrossDomainMessengerImpl(dii, dio); + assertEq(srL1CrossDomainMessengerImpl, address(dio.l1CrossDomainMessengerImpl())); + + address srL1ERC721BridgeImpl = address(0xAE2AF01232a6c4a4d3012C5eC5b1b35059caF10d); + vm.etch(address(srL1ERC721BridgeImpl), hex"01"); + deployImplementations.deployL1ERC721BridgeImpl(dii, dio); + assertEq(srL1ERC721BridgeImpl, address(dio.l1ERC721BridgeImpl())); + + address srL1StandardBridgeImpl = address(0x64B5a5Ed26DCb17370Ff4d33a8D503f0fbD06CfF); + vm.etch(address(srL1StandardBridgeImpl), hex"01"); + deployImplementations.deployL1StandardBridgeImpl(dii, dio); + assertEq(srL1StandardBridgeImpl, address(dio.l1StandardBridgeImpl())); + + address srOptimismMintableERC20FactoryImpl = address(0xE01efbeb1089D1d1dB9c6c8b135C934C0734c846); + vm.etch(address(srOptimismMintableERC20FactoryImpl), hex"01"); + deployImplementations.deployOptimismMintableERC20FactoryImpl(dii, dio); + assertEq(srOptimismMintableERC20FactoryImpl, address(dio.optimismMintableERC20FactoryImpl())); + + address srOptimismPortalImpl = address(0xe2F826324b2faf99E513D16D266c3F80aE87832B); + vm.etch(address(srOptimismPortalImpl), hex"01"); + deployImplementations.deployOptimismPortalImpl(dii, dio); + assertEq(srOptimismPortalImpl, address(dio.optimismPortalImpl())); + + address srDelayedWETHImpl = address(0x71e966Ae981d1ce531a7b6d23DC0f27B38409087); + vm.etch(address(srDelayedWETHImpl), hex"01"); + deployImplementations.deployDelayedWETHImpl(dii, dio); + assertEq(srDelayedWETHImpl, address(dio.delayedWETHImpl())); + + address srPreimageOracleSingleton = address(0x9c065e11870B891D214Bc2Da7EF1f9DDFA1BE277); + vm.etch(address(srPreimageOracleSingleton), hex"01"); + deployImplementations.deployPreimageOracleSingleton(dii, dio); + assertEq(srPreimageOracleSingleton, address(dio.preimageOracleSingleton())); + + address srMipsSingleton = address(0x16e83cE5Ce29BF90AD9Da06D2fE6a15d5f344ce4); + vm.etch(address(srMipsSingleton), hex"01"); + deployImplementations.deployMipsSingleton(dii, dio); + assertEq(srMipsSingleton, address(dio.mipsSingleton())); + + address srDisputeGameFactoryImpl = address(0xc641A33cab81C559F2bd4b21EA34C290E2440C2B); + vm.etch(address(srDisputeGameFactoryImpl), hex"01"); + deployImplementations.deployDisputeGameFactoryImpl(dii, dio); + assertEq(srDisputeGameFactoryImpl, address(dio.disputeGameFactoryImpl())); + } + + function test_deployAtNonExistentRelease_reverts() public { + string memory unknownRelease = "op-contracts/v0.0.0"; + dii.set(dii.release.selector, unknownRelease); + + bytes memory expectedErr = + bytes(string.concat("DeployImplementations: failed to deploy release ", unknownRelease)); + + vm.expectRevert(expectedErr); + deployImplementations.deploySystemConfigImpl(dii, dio); + + vm.expectRevert(expectedErr); + deployImplementations.deployL1CrossDomainMessengerImpl(dii, dio); + + vm.expectRevert(expectedErr); + deployImplementations.deployL1ERC721BridgeImpl(dii, dio); + + vm.expectRevert(expectedErr); + deployImplementations.deployL1StandardBridgeImpl(dii, dio); + + vm.expectRevert(expectedErr); + deployImplementations.deployOptimismMintableERC20FactoryImpl(dii, dio); + + // TODO: Uncomment the code below when OPContractsManager is deployed based on release. Superchain-registry + // doesn't contain OPContractsManager yet. + // dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy)); + // dii.set(dii.protocolVersionsProxy.selector, address(protocolVersionsProxy)); + // vm.etch(address(superchainConfigProxy), hex"01"); + // vm.etch(address(protocolVersionsProxy), hex"01"); + // vm.expectRevert(expectedErr); + // deployImplementations.deployOPContractsManagerImpl(dii, dio); + + dii.set(dii.proofMaturityDelaySeconds.selector, 1); + dii.set(dii.disputeGameFinalityDelaySeconds.selector, 2); + vm.expectRevert(expectedErr); + deployImplementations.deployOptimismPortalImpl(dii, dio); + + dii.set(dii.withdrawalDelaySeconds.selector, 1); + vm.expectRevert(expectedErr); + deployImplementations.deployDelayedWETHImpl(dii, dio); + + dii.set(dii.minProposalSizeBytes.selector, 1); + dii.set(dii.challengePeriodSeconds.selector, 2); + vm.expectRevert(expectedErr); + deployImplementations.deployPreimageOracleSingleton(dii, dio); + + address preImageOracleSingleton = makeAddr("preImageOracleSingleton"); + vm.etch(address(preImageOracleSingleton), hex"01"); + dio.set(dio.preimageOracleSingleton.selector, preImageOracleSingleton); + vm.expectRevert(expectedErr); + deployImplementations.deployMipsSingleton(dii, dio); + + vm.expectRevert(expectedErr); // fault proof contracts don't exist at this release + deployImplementations.deployDisputeGameFactoryImpl(dii, dio); + } + + function test_noContractExistsAtRelease_reverts() public { + string memory unknownRelease = "op-contracts/v1.3.0"; + dii.set(dii.release.selector, unknownRelease); + bytes memory expectedErr = + bytes(string.concat("DeployImplementations: failed to deploy release ", unknownRelease)); + + vm.expectRevert(expectedErr); // fault proof contracts don't exist at this release + deployImplementations.deployDisputeGameFactoryImpl(dii, dio); + } + function testFuzz_run_memory_succeeds(bytes32 _seed) public { withdrawalDelaySeconds = uint256(hash(_seed, 0)); minProposalSizeBytes = uint256(hash(_seed, 1)); challengePeriodSeconds = bound(uint256(hash(_seed, 2)), 0, type(uint64).max); proofMaturityDelaySeconds = uint256(hash(_seed, 3)); disputeGameFinalityDelaySeconds = uint256(hash(_seed, 4)); - release = string(bytes.concat(hash(_seed, 5))); + string memory release = string(bytes.concat(hash(_seed, 5))); protocolVersionsProxy = ProtocolVersions(address(uint160(uint256(hash(_seed, 7))))); // Must configure the ProxyAdmin contract which is used to upgrade the OPSM's proxy contract. @@ -325,6 +462,7 @@ contract DeployImplementations_Test is Test { dii.set(dii.challengePeriodSeconds.selector, challengePeriodSeconds); dii.set(dii.proofMaturityDelaySeconds.selector, proofMaturityDelaySeconds); dii.set(dii.disputeGameFinalityDelaySeconds.selector, disputeGameFinalityDelaySeconds); + string memory release = "dev-release"; dii.set(dii.release.selector, release); dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy)); dii.set(dii.protocolVersionsProxy.selector, address(protocolVersionsProxy)); diff --git a/packages/contracts-bedrock/test/DeployOPChain.t.sol b/packages/contracts-bedrock/test/DeployOPChain.t.sol index ef8fc06cc6264..5f4525fde158b 100644 --- a/packages/contracts-bedrock/test/DeployOPChain.t.sol +++ b/packages/contracts-bedrock/test/DeployOPChain.t.sol @@ -323,7 +323,7 @@ contract DeployOPChain_TestBase is Test { uint256 challengePeriodSeconds = 300; uint256 proofMaturityDelaySeconds = 400; uint256 disputeGameFinalityDelaySeconds = 500; - string release = "op-contracts/latest"; + string release = "dev-release"; // this means implementation contracts will be deployed SuperchainConfig superchainConfigProxy; ProtocolVersions protocolVersionsProxy; @@ -393,6 +393,11 @@ contract DeployOPChain_TestBase is Test { dii.set(dii.release.selector, release); dii.set(dii.superchainConfigProxy.selector, address(superchainConfigProxy)); dii.set(dii.protocolVersionsProxy.selector, address(protocolVersionsProxy)); + // End users of the DeployImplementations contract will need to set the `standardVersionsToml`. + string memory standardVersionsTomlPath = + string.concat(vm.projectRoot(), "/test/fixtures/standard-versions.toml"); + string memory standardVersionsToml = vm.readFile(standardVersionsTomlPath); + dii.set(dii.standardVersionsToml.selector, standardVersionsToml); deployImplementations.run(dii, dio); // Set the OPStackManager input for DeployOPChain. diff --git a/packages/contracts-bedrock/test/fixtures/standard-versions.toml b/packages/contracts-bedrock/test/fixtures/standard-versions.toml new file mode 100644 index 0000000000000..cb4d336a73364 --- /dev/null +++ b/packages/contracts-bedrock/test/fixtures/standard-versions.toml @@ -0,0 +1,47 @@ +standard_release = "op-contracts/v1.6.0" + +[releases] + +# Contracts which are +# * unproxied singletons: specify a standard "address" +# * proxied : specify a standard "implementation_address" +# * neither : specify neither a standard "address" nor "implementation_address" + +# Fault Proofs https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.6.0 +[releases."op-contracts/v1.6.0"] +optimism_portal = { version = "3.10.0", implementation_address = "0xe2F826324b2faf99E513D16D266c3F80aE87832B" } +system_config = { version = "2.2.0", implementation_address = "0xF56D96B2535B932656d3c04Ebf51baBff241D886" } +anchor_state_registry = { version = "2.0.0" } +delayed_weth = { version = "1.1.0", implementation_address = "0x71e966Ae981d1ce531a7b6d23DC0f27B38409087" } +dispute_game_factory = { version = "1.0.0", implementation_address = "0xc641A33cab81C559F2bd4b21EA34C290E2440C2B" } +fault_dispute_game = { version = "1.3.0" } +permissioned_dispute_game = { version = "1.3.0" } +mips = { version = "1.1.0", address = "0x16e83cE5Ce29BF90AD9Da06D2fE6a15d5f344ce4" } +preimage_oracle = { version = "1.1.2", address = "0x9c065e11870B891D214Bc2Da7EF1f9DDFA1BE277" } +l1_cross_domain_messenger = { version = "2.3.0", implementation_address = "0xD3494713A5cfaD3F5359379DfA074E2Ac8C6Fd65" } +l1_erc721_bridge = { version = "2.1.0", implementation_address = "0xAE2AF01232a6c4a4d3012C5eC5b1b35059caF10d" } +l1_standard_bridge = { version = "2.1.0", implementation_address = "0x64B5a5Ed26DCb17370Ff4d33a8D503f0fbD06CfF" } +# l2_output_oracle -- This contract not used in fault proofs +optimism_mintable_erc20_factory = { version = "1.9.0", implementation_address = "0xE01efbeb1089D1d1dB9c6c8b135C934C0734c846" } + +# Fault Proofs https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.4.0 +[releases."op-contracts/v1.4.0"] +optimism_portal = { version = "3.10.0", implementation_address = "0xe2F826324b2faf99E513D16D266c3F80aE87832B" } +system_config = { version = "2.2.0", implementation_address = "0xF56D96B2535B932656d3c04Ebf51baBff241D886" } +anchor_state_registry = { version = "1.0.0" } +delayed_weth = { version = "1.0.0", implementation_address = "0x97988d5624F1ba266E1da305117BCf20713bee08" } +dispute_game_factory = { version = "1.0.0", implementation_address = "0xc641A33cab81C559F2bd4b21EA34C290E2440C2B" } +fault_dispute_game = { version = "1.2.0" } +permissioned_dispute_game = { version = "1.2.0" } +mips = { version = "1.0.1", address = "0x0f8EdFbDdD3c0256A80AD8C0F2560B1807873C9c" } +preimage_oracle = { version = "1.0.0", address = "0xD326E10B8186e90F4E2adc5c13a2d0C137ee8b34" } + +# MCP https://github.com/ethereum-optimism/optimism/releases/tag/op-contracts%2Fv1.3.0 +[releases."op-contracts/v1.3.0"] +l1_cross_domain_messenger = { version = "2.3.0", implementation_address = "0xD3494713A5cfaD3F5359379DfA074E2Ac8C6Fd65" } +l1_erc721_bridge = { version = "2.1.0", implementation_address = "0xAE2AF01232a6c4a4d3012C5eC5b1b35059caF10d" } +l1_standard_bridge = { version = "2.1.0", implementation_address = "0x64B5a5Ed26DCb17370Ff4d33a8D503f0fbD06CfF" } +l2_output_oracle = { version = "1.8.0", implementation_address = "0xF243BEd163251380e78068d317ae10f26042B292" } +optimism_mintable_erc20_factory = { version = "1.9.0", implementation_address = "0xE01efbeb1089D1d1dB9c6c8b135C934C0734c846" } +optimism_portal = { version = "2.5.0", implementation_address = "0x2D778797049FE9259d947D1ED8e5442226dFB589" } +system_config = { version = "1.12.0", implementation_address = "0xba2492e52F45651B60B8B38d4Ea5E2390C64Ffb1" } diff --git a/packages/contracts-bedrock/test/fixtures/test-deploy-auth-system-in.toml b/packages/contracts-bedrock/test/fixtures/test-deploy-auth-system-in.toml deleted file mode 100644 index 4f0df83e1af2e..0000000000000 --- a/packages/contracts-bedrock/test/fixtures/test-deploy-auth-system-in.toml +++ /dev/null @@ -1,11 +0,0 @@ -[safe] -threshold = 5 -owners = [ - "0x1111111111111111111111111111111111111111", - "0x2222222222222222222222222222222222222222", - "0x3333333333333333333333333333333333333333", - "0x4444444444444444444444444444444444444444", - "0x5555555555555555555555555555555555555555", - "0x6666666666666666666666666666666666666666", - "0x7777777777777777777777777777777777777777" -] diff --git a/packages/contracts-bedrock/test/fixtures/test-deploy-auth-system-out.toml b/packages/contracts-bedrock/test/fixtures/test-deploy-auth-system-out.toml deleted file mode 100644 index 35465cae19424..0000000000000 --- a/packages/contracts-bedrock/test/fixtures/test-deploy-auth-system-out.toml +++ /dev/null @@ -1 +0,0 @@ -safe = "0xDC93f9959c0F9c3849461B6468B4592a19567E09" diff --git a/packages/contracts-bedrock/test/fixtures/test-deploy-superchain-in.toml b/packages/contracts-bedrock/test/fixtures/test-deploy-superchain-in.toml deleted file mode 100644 index 0900e71635d76..0000000000000 --- a/packages/contracts-bedrock/test/fixtures/test-deploy-superchain-in.toml +++ /dev/null @@ -1,8 +0,0 @@ -paused = false -requiredProtocolVersion = 1 -recommendedProtocolVersion = 2 - -[roles] -proxyAdminOwner = "0x51f0348a9fA2aAbaB45E82825Fbd13d406e04497" -protocolVersionsOwner = "0xeEB4cc05dC0dE43c465f97cfc703D165418CA93A" -guardian = "0xE5DbA98c65F4B9EB0aeEBb3674fE64f88509a1eC" diff --git a/packages/contracts-bedrock/test/fixtures/test-deploy-superchain-out.toml b/packages/contracts-bedrock/test/fixtures/test-deploy-superchain-out.toml deleted file mode 100644 index ceb558a79d5a0..0000000000000 --- a/packages/contracts-bedrock/test/fixtures/test-deploy-superchain-out.toml +++ /dev/null @@ -1,5 +0,0 @@ -protocolVersionsImpl = "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9" -protocolVersionsProxy = "0x1d1499e622D69689cdf9004d05Ec547d650Ff211" -superchainConfigImpl = "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a" -superchainConfigProxy = "0xc7183455a4C133Ae270771860664b6B7ec320bB1" -superchainProxyAdmin = "0x2e234DAe75C793f67A35089C9d99245E1C58470b"