Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions op-chain-ops/interopgen/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ func GenesisL2(l2Host *script.Host, cfg *L2Config, deployment *L2Deployment, mul
GasPayingTokenSymbol: cfg.GasPayingTokenSymbol,
NativeAssetLiquidityAmount: cfg.NativeAssetLiquidityAmount.ToInt(),
LiquidityControllerOwner: cfg.LiquidityControllerOwner,
UseL2CM: false, // TODO(#19102): add support for L2CM
}); err != nil {
return fmt.Errorf("failed L2 genesis: %w", err)
}
Expand Down
3 changes: 3 additions & 0 deletions op-deployer/pkg/deployer/devfeatures.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ var (

// OPCMV2DevFlag enables the OPContractsManagerV2 contract.
OPCMV2DevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000010000")

// L2CMDevFlag enables L2CM.
L2CMDevFlag = common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000100000")
)

// IsDevFeatureEnabled checks if a specific development feature is enabled in a feature bitmap.
Expand Down
26 changes: 26 additions & 0 deletions op-deployer/pkg/deployer/integration_test/apply_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,32 @@ func TestEndToEndApply(t *testing.T) {
require.Equal(t, amount, account.Balance, "Native asset liquidity predeploy should have the configured balance")
})

t.Run("with L2CM", func(t *testing.T) {
intent, st := shared.NewIntent(t, l1ChainID, dk, l2ChainID1, loc, loc, testCustomGasLimit)

intent.GlobalDeployOverrides = map[string]any{
"devFeatureBitmap": deployer.L2CMDevFlag,
}

require.NoError(t, deployer.ApplyPipeline(ctx, deployer.ApplyPipelineOpts{
DeploymentTarget: deployer.DeploymentTargetLive,
L1RPCUrl: l1RPC,
DeployerPrivateKey: pk,
Intent: intent,
State: st,
Logger: lgr,
StateWriter: pipeline.NoopStateWriter(),
CacheDir: testCacheDir,
}))

// Check that the conditional deployer predeploy is deployed in L2 genesis
conditionalDeployerAddr := common.HexToAddress("0x420000000000000000000000000000000000002C")
l2Genesis := st.Chains[0].Allocs.Data.Accounts
account, exists := l2Genesis[conditionalDeployerAddr]
require.True(t, exists, "Conditional deployer should exist in L2 genesis")
require.NotEmpty(t, account.Code, "Conditional deployer should have code deployed")
})

t.Run("OPCMV2 deployment", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand Down
1 change: 1 addition & 0 deletions op-deployer/pkg/deployer/opcm/l2genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type L2GenesisInput struct {
GasPayingTokenSymbol string
NativeAssetLiquidityAmount *big.Int
LiquidityControllerOwner common.Address
UseL2CM bool
}

type L2GenesisScript script.DeployScriptWithoutOutput[L2GenesisInput]
Expand Down
16 changes: 12 additions & 4 deletions op-deployer/pkg/deployer/pipeline/l2genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum/go-ethereum/common/hexutil"

"github.com/ethereum-optimism/optimism/op-service/jsonutil"

"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"

"github.com/ethereum-optimism/optimism/op-deployer/pkg/env"
"github.com/ethereum-optimism/optimism/op-service/jsonutil"

"github.com/ethereum-optimism/optimism/op-chain-ops/foundry"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard"
"github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -85,6 +83,15 @@ func GenerateL2Genesis(pEnv *Env, intent *state.Intent, bundle ArtifactsBundle,

cgt := buildCGTConfig(thisIntent)

// Check if L2CM feature is enabled
var useL2CM bool = false
Comment thread
maurelian marked this conversation as resolved.
Outdated
if devFeatureBitmap, ok := intent.GlobalDeployOverrides["devFeatureBitmap"].(common.Hash); ok {
l2CMFlag := common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000100000")
Comment thread
maurelian marked this conversation as resolved.
if isDevFeatureEnabled(devFeatureBitmap, l2CMFlag) {
useL2CM = true
}
}

if err := script.Run(opcm.L2GenesisInput{
L1ChainID: new(big.Int).SetUint64(intent.L1ChainID),
L2ChainID: chainID.Big(),
Expand Down Expand Up @@ -118,6 +125,7 @@ func GenerateL2Genesis(pEnv *Env, intent *state.Intent, bundle ArtifactsBundle,
GasPayingTokenSymbol: cgt.GasPayingTokenSymbol,
NativeAssetLiquidityAmount: cgt.NativeAssetLiquidityAmount,
LiquidityControllerOwner: cgt.LiquidityControllerOwner,
UseL2CM: useL2CM,
}); err != nil {
return fmt.Errorf("failed to call L2Genesis script: %w", err)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { ISemver } from "interfaces/universal/ISemver.sol";

/// @title IConditionalDeployer
/// @notice Interface for the ConditionalDeployer contract.
interface IConditionalDeployer is ISemver {
/// @notice Emitted when an implementation is deployed.
/// @param implementation The address of the deployed implementation.
/// @param salt The salt used for deployment.
event ImplementationDeployed(address indexed implementation, bytes32 salt);

/// @notice Emitted when deployment is skipped because implementation already exists.
/// @param implementation The address of the existing implementation.
event ImplementationExists(address indexed implementation);

/// @notice Error thrown when deployment fails.
error ConditionalDeployer_DeploymentFailed(bytes data);

/// @notice Deploys an implementation using CREATE2 if it doesn't already exist.
/// @param _salt The salt to use for CREATE2 deployment.
/// @param _code The initialization code for the contract.
/// @return implementation_ The address of the deployed or existing implementation.
function deploy(bytes32 _salt, bytes memory _code) external payable returns (address implementation_);

/// @notice Address of the Arachnid's DeterministicDeploymentProxy.
function deterministicDeploymentProxy() external pure returns (address payable deterministicDeploymentProxy_);
}
16 changes: 14 additions & 2 deletions packages/contracts-bedrock/scripts/L2Genesis.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ contract L2Genesis is Script {
string gasPayingTokenSymbol;
uint256 nativeAssetLiquidityAmount;
address liquidityControllerOwner;
bool useL2CM;
}

using ForkUtils for Fork;
Expand Down Expand Up @@ -221,8 +222,11 @@ contract L2Genesis is Script {
vm.etch(addr, code);
EIP1967Helper.setAdmin(addr, Predeploys.PROXY_ADMIN);

if (Predeploys.isSupportedPredeploy(addr, _input.fork, _input.deployCrossL2Inbox, _input.useCustomGasToken))
{
if (
Predeploys.isSupportedPredeploy(
addr, _input.fork, _input.deployCrossL2Inbox, _input.useCustomGasToken, _input.useL2CM
)
) {
address implementation = Predeploys.predeployToCodeNamespace(addr);
EIP1967Helper.setImplementation(addr, implementation);
}
Expand Down Expand Up @@ -268,6 +272,9 @@ contract L2Genesis is Script {
setLiquidityController(_input); // 29
setNativeAssetLiquidity(_input); // 2A
}
if (_input.useL2CM) {
setConditionalDeployer(); // 2C
}
Comment thread
maurelian marked this conversation as resolved.
}

function setInteropPredeployProxies() internal { }
Expand Down Expand Up @@ -578,6 +585,11 @@ contract L2Genesis is Script {
vm.deal(Predeploys.NATIVE_ASSET_LIQUIDITY, _input.nativeAssetLiquidityAmount);
}

/// @notice This predeploy is following the safety invariant #1.
Comment thread
maurelian marked this conversation as resolved.
function setConditionalDeployer() internal {
_setImplementationCode(Predeploys.CONDITIONAL_DEPLOYER);
}

/// @notice Sets all the preinstalls.
function setPreinstalls() internal {
address tmpSetPreinstalls = address(uint160(uint256(keccak256("SetPreinstalls"))));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ contract DeployConfig is Script {
uint256 public faultGameV2ClockExtension;
uint256 public faultGameV2MaxClockDuration;

bool public useL2CM;

bool public useInterop;
bool public useUpgradedFork;
bytes32 public devFeatureBitmap;
Expand Down Expand Up @@ -181,6 +183,8 @@ contract DeployConfig is Script {
daBondSize = _readOr(_json, "$.daBondSize", 1000000000);
daResolverRefundPercentage = _readOr(_json, "$.daResolverRefundPercentage", 0);

useL2CM = _readOr(_json, "$.useL2CM", false);

useInterop = _readOr(_json, "$.useInterop", false);
devFeatureBitmap = bytes32(_readOr(_json, "$.devFeatureBitmap", 0));
useUpgradedFork;
Expand Down Expand Up @@ -317,6 +321,11 @@ contract DeployConfig is Script {
operatorFeeVaultWithdrawalNetwork = _operatorFeeVaultWithdrawalNetwork;
}

/// @notice Allow the `useL2CM` config to be overridden in testing environments
function setUseL2CM(bool _useL2CM) public {
useL2CM = _useL2CM;
}

function latestGenesisFork() internal view returns (Fork) {
if (l2GenesisJovianTimeOffset == 0) {
return Fork.JOVIAN;
Expand Down
5 changes: 5 additions & 0 deletions packages/contracts-bedrock/scripts/libraries/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,11 @@ library Config {
return vm.envOr("DEV_FEATURE__OPCM_V2", false);
}

/// @notice Returns true if the development feature l2cm is enabled.
function devFeatureL2CM() internal view returns (bool) {
return vm.envOr("DEV_FEATURE__L2CM", false);
}

/// @notice Returns true if the system feature custom_gas_token is enabled.
function sysFeatureCustomGasToken() internal view returns (bool) {
return vm.envOr("SYS_FEATURE__CUSTOM_GAS_TOKEN", false);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
[
{
"inputs": [
{
"internalType": "bytes32",
"name": "_salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "_code",
"type": "bytes"
}
],
"name": "deploy",
"outputs": [
{
"internalType": "address",
"name": "implementation_",
"type": "address"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "deterministicDeploymentProxy",
"outputs": [
{
"internalType": "address payable",
"name": "deterministicDeploymentProxy_",
"type": "address"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "implementation",
"type": "address"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "ImplementationDeployed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "implementation",
"type": "address"
}
],
"name": "ImplementationExists",
"type": "event"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "ConditionalDeployer_DeploymentFailed",
"type": "error"
}
]
4 changes: 4 additions & 0 deletions packages/contracts-bedrock/snapshots/semver-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@
"initCodeHash": "0x838bbd7f381e84e21887f72bd1da605bfc4588b3c39aed96cbce67c09335b3ee",
"sourceCodeHash": "0xcb329746df0baddd3dc03c6c88da5d6bdc0f0a96d30e6dc78d0891bb1e935032"
},
"src/L2/ConditionalDeployer.sol:ConditionalDeployer": {
"initCodeHash": "0x79dac7a68e116941ebc9dea7711e57ad4ef878f62d672beaa26393ec39770ccb",
"sourceCodeHash": "0x1e751dcdc7a033da0dbe3041a3e6696757ae943e87c5a9e05b8138e9d4e90604"
},
"src/L2/CrossL2Inbox.sol:CrossL2Inbox": {
"initCodeHash": "0x56f868e561c4abe539043f98b16aad9305479e68fd03ece2233249b0c73a24ea",
"sourceCodeHash": "0x7c6d362a69a480a06a079542a7fd2ce48cb1dd80d6b9043fba60218569371349"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
67 changes: 67 additions & 0 deletions packages/contracts-bedrock/src/L2/ConditionalDeployer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;

// Interfaces
import { ISemver } from "interfaces/universal/ISemver.sol";

/// @custom:proxied true
/// @custom:predeploy 0x420000000000000000000000000000000000002C
/// @title ConditionalDeployer
/// @notice ConditionalDeployer is used to deploy implementations for predeploys during network upgrades.
/// It uses Arachnid's DeterministicDeploymentProxy to deploy the implementations.
contract ConditionalDeployer is ISemver {
/// @notice Emitted when an implementation is deployed.
/// @param implementation The address of the deployed implementation.
/// @param salt The salt used for deployment.
event ImplementationDeployed(address indexed implementation, bytes32 salt);

/// @notice Emitted when deployment is skipped because implementation already exists.
/// @param implementation The address of the existing implementation.
event ImplementationExists(address indexed implementation);

/// @notice Error thrown when deployment fails.
error ConditionalDeployer_DeploymentFailed(bytes data);

/// @notice Address of the Arachnid DeterministicDeploymentProxy.
address payable internal constant DETERMINISTIC_DEPLOYMENT_PROXY =
payable(0x4e59b44847b379578588920cA78FbF26c0B4956C);

/// @notice Semantic version.
/// @custom:semver 1.0.0
string public constant version = "1.0.0";

/// @notice Deploys an implementation using CREATE2 if it doesn't already exist.
/// @param _salt The salt to use for CREATE2 deployment.
/// @param _code The initialization code for the contract.
/// @return implementation_ The address of the deployed or existing implementation.
function deploy(bytes32 _salt, bytes memory _code) external payable returns (address implementation_) {
Comment thread
0xiamflux marked this conversation as resolved.
Outdated
// Compute the address where the contract will be deployed using CREATE2 formula
bytes32 codeHash = keccak256(_code);
implementation_ = address(
uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), DETERMINISTIC_DEPLOYMENT_PROXY, _salt, codeHash))))
);

// Check if implementation already exists
if (implementation_.code.length != 0) {
emit ImplementationExists(implementation_);
return implementation_;
}

// Deploy using Arachnid's DeterministicDeploymentProxy
// Calldata format: salt + initcode
(bool success, bytes memory data) =
DETERMINISTIC_DEPLOYMENT_PROXY.call{ value: msg.value }(abi.encodePacked(_salt, _code));
Comment thread
0xiamflux marked this conversation as resolved.
Outdated
if (!success) {
revert ConditionalDeployer_DeploymentFailed(data);
}
Comment thread
maurelian marked this conversation as resolved.

emit ImplementationDeployed(implementation_, _salt);
return implementation_;
}

/// @notice Returns the address of the Arachnid's DeterministicDeploymentProxy.
/// @return deterministicDeploymentProxy_ The address of the Arachnid's DeterministicDeploymentProxy.
function deterministicDeploymentProxy() external pure returns (address payable deterministicDeploymentProxy_) {
Comment thread
maurelian marked this conversation as resolved.
Outdated
deterministicDeploymentProxy_ = DETERMINISTIC_DEPLOYMENT_PROXY;
}
}
Loading
Loading