Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
88cedeb
feat: add nut lib
0xniha Feb 17, 2026
bd8101e
refactor: remove newTx function from nut lib
0xniha Feb 19, 2026
62051a8
Merge branch 'develop' into feat/l2cm-l2-fork-test
0xniha Feb 19, 2026
e07b3bd
feat: add GenerateNUTBundle script
0xniha Feb 19, 2026
8cd167f
fix: pre-pr fixes
0xniha Feb 19, 2026
25e5fd4
test: add generate bundle utils tests
0xniha Feb 20, 2026
48392a9
refactor: replace custom computeCreate2Address by forge's
0xniha Feb 24, 2026
262d166
docs: fix and add natspec
0xniha Feb 24, 2026
d65d2d3
fix: remove script inheritance from utils contract
0xniha Feb 24, 2026
aeda471
refactor: remove unnecesary fields from nut tx struct & replace sourc…
0xniha Feb 24, 2026
82b6538
test: add create tx empry args equivalence and different salts assump…
0xniha Feb 24, 2026
1aed88f
refactor: remove fork, salt and cgt from bundle script input & consol…
0xniha Feb 25, 2026
a0b8f80
fix: rm L2ContractsManagerTypes
0xniha Feb 25, 2026
e48a7c4
Merge branch 'develop' into chore/l2cm-bundle-sync
0xniha Feb 25, 2026
3ce699f
fix: update impl struct and make lib functions internal
0xniha Feb 25, 2026
66e55f8
chore: sync nut bundle with develop
0xniha Feb 25, 2026
b98df15
feat: remove TODO for L2CM merge and add TODO for OptimismMintableERC…
0xniha Feb 25, 2026
8f55173
test: complete nuts structure testing
0xniha Feb 25, 2026
a7186ea
feat: add metadata struct to bundle
0xniha Feb 26, 2026
4e6c17e
feat: make OptimismMintableERC721Factory initializable
0xniha Feb 27, 2026
66ce05e
fix: replace jovian for karst
0xniha Feb 27, 2026
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
3 changes: 3 additions & 0 deletions packages/contracts-bedrock/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ deployments/1-deploy.json

# Getting Started guide deploy config
deploy-config/getting-started.json

# Nut test deployment
deployments/nut-*.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ interface IOptimismMintableERC721Factory {
function remoteChainID() external view returns (uint256);
function version() external view returns (string memory);

function __constructor__(address _bridge, uint256 _remoteChainId) external;
function initialize(address _bridge, uint256 _remoteChainID) external;
}
23 changes: 7 additions & 16 deletions packages/contracts-bedrock/scripts/L2Genesis.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -360,25 +360,16 @@ contract L2Genesis is Script {
});
}

/// @notice This predeploy is following the safety invariant #2,
/// @notice This predeploy is following the safety invariant #1.
function setOptimismMintableERC721Factory(Input memory _input) internal {
IOptimismMintableERC721Factory factory = IOptimismMintableERC721Factory(
DeployUtils.create1({
_name: "OptimismMintableERC721Factory",
_args: DeployUtils.encodeConstructor(
abi.encodeCall(
IOptimismMintableERC721Factory.__constructor__, (Predeploys.L2_ERC721_BRIDGE, _input.l1ChainID)
)
)
})
);
address impl = _setImplementationCode(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY);

address impl = Predeploys.predeployToCodeNamespace(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY);
vm.etch(impl, address(factory).code);
IOptimismMintableERC721Factory(impl).initialize({ _bridge: address(0), _remoteChainID: 0 });

/// Reset so its not included state dump
vm.etch(address(factory), "");
vm.resetNonce(address(factory));
IOptimismMintableERC721Factory(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY).initialize({
_bridge: Predeploys.L2_ERC721_BRIDGE,
_remoteChainID: _input.l1ChainID
});
}

/// @notice This predeploy is following the safety invariant #1.
Expand Down
219 changes: 219 additions & 0 deletions packages/contracts-bedrock/scripts/libraries/UpgradeUtils.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Libraries
import { Vm } from "forge-std/Vm.sol";
import { NetworkUpgradeTxns } from "src/libraries/NetworkUpgradeTxns.sol";
import { Preinstalls } from "src/libraries/Preinstalls.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { Constants } from "src/libraries/Constants.sol";

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

// Contracts
import { ConditionalDeployer } from "src/L2/ConditionalDeployer.sol";

/// @title UpgradeUtils
/// @notice Utility library for L2 hardfork upgrade transaction generation.
library UpgradeUtils {
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));

/// @notice The number of implementations deployed in every upgrade.
/// Includes:
/// - 1 StorageSetter
/// - 16 base predeploys
/// - 7 INTEROP predeploys
/// - 2 CGT predeploys (NativeAssetLiquidity, LiquidityController)
/// - 2 CGT variants (L1BlockCGT, L2ToL1MessagePasserCGT)
/// Total: 28 implementations
uint256 internal constant IMPLEMENTATION_COUNT = 28;

/// @notice The default gas limit for a deployment transaction.
uint64 internal constant DEFAULT_DEPLOYMENT_GAS = 375_000;

/// @notice The default gas limit for an upgrade transaction.
uint64 internal constant DEFAULT_UPGRADE_GAS = 50_000;

/// @notice Gas limits for different types of upgrade transactions.
/// @param conditionalDeployerDeployment Gas for deploying ConditionalDeployer
/// @param conditionalDeployerUpgrade Gas for upgrading ConditionalDeployer proxy
/// @param proxyAdminUpgrade Gas for upgrading ProxyAdmin implementation
/// @param l2cmDeployment Gas for deploying L2ContractsManager
/// @param upgradeExecution Gas for L2ProxyAdmin.upgradePredeploys() call
struct GasLimits {
// Fixed
uint64 l2cmDeployment;
uint64 upgradeExecution;
// Karst
uint64 conditionalDeployerDeployment;
uint64 conditionalDeployerUpgrade;
uint64 proxyAdminUpgrade;
}

/// @notice Returns the total number of transactions for the current upgrade.
/// @dev Total count:
/// - 28 implementation deployments
/// - [KARST]2 ConditionalDeployer (deployment + upgrade)
/// - [KARST] 1 ProxyAdmin upgrade
/// - 1 L2CM deployment
/// - 1 Upgrade Predeploys call
function getTransactionCount() internal pure returns (uint256 txnCount_) {
txnCount_ = IMPLEMENTATION_COUNT + 5;
}

/// @notice Returns the gas limits for all upgrade transaction types.
/// @dev Gas limits are chosen to provide sufficient headroom while being
/// conservative enough to fit within the upgrade block gas allocation.
/// Rationale for each limit:
/// - TODO: Add rationale here
/// @return Gas limits struct.
function gasLimits() internal pure returns (GasLimits memory) {
return GasLimits({
// Fixed
l2cmDeployment: DEFAULT_DEPLOYMENT_GAS,
upgradeExecution: type(uint64).max,
// Karst
conditionalDeployerDeployment: DEFAULT_DEPLOYMENT_GAS,
conditionalDeployerUpgrade: DEFAULT_UPGRADE_GAS,
proxyAdminUpgrade: DEFAULT_UPGRADE_GAS
});
}

/// @notice Returns the array of predeploy names to upgrade.
/// @dev Exception: StorageSetter is not a predeploy, but is upgraded in L2CM too.
/// @return implementations_ Array of implementation names to upgrade.
function getImplementationsNamesToUpgrade() internal pure returns (string[] memory implementations_) {
implementations_ = new string[](IMPLEMENTATION_COUNT);

// StorageSetter
implementations_[0] = "StorageSetter";

// Base predeploys
implementations_[1] = "L2CrossDomainMessenger";
implementations_[2] = "GasPriceOracle";
implementations_[3] = "L2StandardBridge";
implementations_[4] = "SequencerFeeVault";
implementations_[5] = "OptimismMintableERC20Factory";
implementations_[6] = "L2ERC721Bridge";
implementations_[7] = "L1Block";
implementations_[8] = "L2ToL1MessagePasser";
implementations_[9] = "OptimismMintableERC721Factory";
implementations_[10] = "L2ProxyAdmin";
implementations_[11] = "BaseFeeVault";
implementations_[12] = "L1FeeVault";
implementations_[13] = "OperatorFeeVault";
implementations_[14] = "SchemaRegistry";
implementations_[15] = "EAS";
implementations_[16] = "FeeSplitter";

// INTEROP predeploys
implementations_[17] = "CrossL2Inbox";
implementations_[18] = "L2ToL2CrossDomainMessenger";
implementations_[19] = "SuperchainETHBridge";
implementations_[20] = "OptimismSuperchainERC20Factory";
implementations_[21] = "OptimismSuperchainERC20Beacon";
implementations_[22] = "SuperchainTokenBridge";
implementations_[23] = "ETHLiquidity";

// CGT predeploys
implementations_[24] = "L1BlockCGT";
implementations_[25] = "L2ToL1MessagePasserCGT";
implementations_[26] = "LiquidityController";
implementations_[27] = "NativeAssetLiquidity";
}

/// @notice Uses vm.computeCreate2Address to compute the CREATE2 address for given initcode and salt.
/// @dev Uses the DeterministicDeploymentProxy address as the deployer.
/// @param _code The contract initcode (creation bytecode).
/// @param _salt The CREATE2 salt.
/// @return expected_ The computed contract address.
function computeCreate2Address(bytes memory _code, bytes32 _salt) internal pure returns (address expected_) {
expected_ = vm.computeCreate2Address(_salt, keccak256(_code), Preinstalls.DeterministicDeploymentProxy);
}

/// @notice Creates a deployment transaction via ConditionalDeployer.
/// @dev The transaction calls ConditionalDeployer.deploy(salt, code) which performs
/// idempotent CREATE2 deployment via the DeterministicDeploymentProxy.
/// @param _upgradeName Human-readable upgrade name (e.g., "Karst").
/// @param _name Human-readable name for the contract being deployed.
/// @param _artifactPath Forge artifact path (e.g., "MyContract.sol:MyContract").
/// @param _salt CREATE2 salt for address computation.
/// @param _gasLimit Gas limit for the deployment transaction.
/// @return txn_ The constructed deployment transaction.
function createDeploymentTxn(
string memory _upgradeName,
string memory _name,
string memory _artifactPath,
bytes32 _salt,
uint64 _gasLimit
)
internal
view
returns (NetworkUpgradeTxns.NetworkUpgradeTxn memory txn_)
{
return createDeploymentTxnWithArgs(_upgradeName, _name, _artifactPath, "", _salt, _gasLimit);
}

/// @notice Creates a deployment transaction via ConditionalDeployer with constructor arguments.
/// @dev The transaction calls ConditionalDeployer.deploy(salt, code) which performs
/// idempotent CREATE2 deployment via the DeterministicDeploymentProxy.
/// @param _upgradeName Human-readable upgrade name (e.g., "Karst").
/// @param _name Human-readable name for the contract being deployed.
/// @param _artifactPath Forge artifact path (e.g., "MyContract.sol:MyContract").
/// @param _args ABI-encoded constructor arguments.
/// @param _salt CREATE2 salt for address computation.
/// @param _gasLimit Gas limit for the deployment transaction.
/// @return txn_ The constructed deployment transaction.
function createDeploymentTxnWithArgs(
string memory _upgradeName,
string memory _name,
string memory _artifactPath,
bytes memory _args,
bytes32 _salt,
uint64 _gasLimit
)
internal
view
returns (NetworkUpgradeTxns.NetworkUpgradeTxn memory txn_)
{
bytes memory code = abi.encodePacked(vm.getCode(_artifactPath), _args);
txn_ = NetworkUpgradeTxns.NetworkUpgradeTxn({
intent: string.concat(_upgradeName, ": Deploy ", _name, " Implementation"),
from: Constants.DEPOSITOR_ACCOUNT,
to: Predeploys.CONDITIONAL_DEPLOYER,
gasLimit: _gasLimit,
data: abi.encodeCall(ConditionalDeployer.deploy, (_salt, code))
});
}

/// @notice Creates an upgrade transaction for a proxy contract.
/// @dev The transaction calls IProxy(proxy).upgradeTo(implementation).
/// For the ProxyAdmin upgrade, the sender must be address(0) to use the
/// zero-address upgrade path in the Proxy.sol implementation.
/// @param _upgradeName Human-readable upgrade name (e.g., "Karst").
/// @param _name Human-readable name for the contract being upgraded.
/// @param _proxy Address of the proxy contract.
/// @param _implementation Address of the new implementation.
/// @param _gasLimit Gas limit for the upgrade transaction.
/// @return txn_ The constructed upgrade transaction.
function createUpgradeTxn(
string memory _upgradeName,
string memory _name,
address _proxy,
address _implementation,
uint64 _gasLimit
)
internal
pure
returns (NetworkUpgradeTxns.NetworkUpgradeTxn memory txn_)
{
txn_ = NetworkUpgradeTxns.NetworkUpgradeTxn({
intent: string.concat(_upgradeName, ": Upgrade ", _name, " Implementation"),
from: address(0),
to: _proxy,
gasLimit: _gasLimit,
data: abi.encodeCall(IProxy.upgradeTo, (_implementation))
});
}
}
Loading