feat: Support custom chains in the CLI#731
Open
yahgwai wants to merge 37 commits into
Open
Conversation
- Add deployProxyAdmin: deploys an OpenZeppelin ProxyAdmin on the orbit chain. - Add deployExpressLaneAuction: deploys the Timeboost ExpressLaneAuction implementation and an initialized TransparentUpgradeableProxy, given a ProxyAdmin. - Expose both as CLI commands. - Add @arbitrum/nitro-contracts ^3.2.0 as the source of the contract ABIs/bytecode.
Drop the proxyAdmin bytecode pre-flight check and its test: no other SDK deploy function validates caller-supplied contract addresses, and address format is already validated by the CLI schema. Also collapse the 12 init-arg fields into a rest-spread rather than re-listing them into the encoder.
Make findChain synthesize a minimal chain on a registry miss instead of throwing, so every CLI call site works against freshly deployed orbit chains not yet in the registry. Removes the redundant findOrDefineChain and consolidates to a single resolver.
Make contracts.rollupCreator/tokenBridgeCreator optional in registerCustomParentChain (requiredness is enforced per-command at the CLI schema layer), validating any provided address as non-zero. Flip chain resolution to custom-first in findChain and getParentChainFromId so a registered custom chain wins over a built-in with the same id, ensuring its factory addresses are used instead of the contract-less built-in entry.
Deploy a standard WETH9 (TestWETH9 from token-bridge-contracts, a full WETH9 with deposit/withdraw) on the chain the wallet client points at, so a freshly created custom parent chain can supply an l1Weth to deployTokenBridgeCreator. Unit test asserts the artifact resolves with the expected constructor/interface; integration test deploys on the testnode and checks metadata + a deposit round-trip.
Port nitro-contracts deployAllContracts (v3.2, BOLD) to viem: deploy the eth and erc20 bridge templates, BridgeCreator, the OSP stack, challenge/rollup logic, UpgradeExecutor, ValidatorWalletCreator and DeployHelper, then a RollupCreator whose constructor wires them directly. Add @offchainlabs/upgrade-executor as a direct dependency for the UpgradeExecutor bytecode. reader4844 follows the SequencerInbox constructor's requirement: address(0) on Arbitrum host chains (detected via the ArbSys precompile), the dead-address sentinel otherwise; Ethereum L1 deployment is out of scope. Unit test round-trips the BridgeCreator template tuples and locks the RollupCreator/SequencerInbox constructor arg order; integration test reads every template back off the deployed RollupCreator.
Port token-bridge-contracts deployL1TokenBridgeCreator to viem: deploy the creator and retryable sender behind proxies, the seven L1 gateway/router templates plus an UpgradeExecutor, the six L2 bytecode-carrier contracts, and the multicalls; initialize each template with throwaway data to lock it; wire the retryable sender via the creator's initialize; then setTemplates. Keeps token-bridge-contracts at 1.2.2 (already installed). Extract shared viem-deploy plumbing into src/utils/deployContract.ts, reused by deployRollupCreator. Retire the Docker-based token-bridge-creator test helper and point createTokenBridge.integration.test.ts at the new SDK function. Unit test round-trips setTemplates args to lock arg order and the L1Templates struct; integration test reads the creator's templates back.
Add CLI deploy commands for the three new factory functions (deployWeth, deployRollupCreator, deployTokenBridgeCreator) via a withWalletClient transform. Add a composable registerCustomParentChainFromInput step and a parentChainContracts schema fragment (per-command required contracts), wired into createRollup (rollupCreator) and createTokenBridge (tokenBridgeCreator + weth) and threaded through the deployNewChain/deployFullChain workflows: a custom parent supplied in JSON is registered before resolution so custom-first findChain returns it with its factory addresses. Update the schemaCoverage mocks (new transforms + deploy fn mocks) and mark the side-effect-only custom fields as covered. Add schema + transform unit tests.
New deployFullChain.integration.test.ts deploys weth/rollupCreator/tokenBridgeCreator on the nitro-testnode L2, registers it as a custom parent chain (shadowing the built-in entry), and deploys a full rollup + token bridge through those freshly deployed factories -- asserting isCustom resolution and populated core + token-bridge contracts. Captures the five timing params from the built-in defaults before registering (the getDefault* helpers reject custom chains). Add a temporary dedicated CI job to run the new deploy tests in isolation for fast feedback; to be removed once green (they also run under test-integration).
createRollupPrepareTransactionRequest also requires params.maxDataSize for a custom parent chain (beyond the five timing params). Supply 104857, the non-L1 value the SequencerInbox templates are deployed with.
The new deploy + custom-parent tests are green in the standard test-integration matrix, so the dedicated fast-feedback job is no longer needed.
Drop restating and type-repeating JSDoc (@param/@returns), section-divider step markers, and redundant inline notes; keep only comments that explain a non-obvious decision, constraint, or invariant.
Drop the per-command-enforcement explanation from registerCustomParentChain's public JSDoc. findChain is internal, so move its leading comment into the body alongside the custom-first note rather than reading as function documentation.
getWethAddress reads contracts.weth on a custom parent, but registration only validated rollupCreator and tokenBridgeCreator. Validate weth the same way, and collapse the three near-identical checks into one local helper.
maxDataSize was hardcoded and had to match the maxDataSize a later createRollup uses, an implicit coupling with no way to align or override. Export DEFAULT_MAX_DATA_SIZE, accept an optional maxDataSize param defaulting to it, expose it on the CLI schema, and reference the constant in the integration test instead of a magic string.
deployWeth, deployProxyAdmin, and deployExpressLaneAuction each inlined the deployContract + waitForTransactionReceipt + revert-check pattern that the deployContractChecked/toDeployContext helper already provides (and that deployRollupCreator/deployTokenBridgeCreator use). Route them through the helper.
The DEFAULT_KEYSET literal was duplicated across deployNewChain and deployFullChain, and the two copies were not equal: deployNewChain's was 9 bytes short of the full key its own length header (0x0121 = 289 bytes) declares, making it malformed. deployFullChain's copy is byte-identical to the SDK keyset test fixtures. Extract the canonical value into a shared module and import it in both, which also fixes deployNewChain's broken default.
Instead of the 0x...dead sentinel, deploy the prebuilt Reader4844 Yul bytecode shipped in @arbitrum/nitro-contracts for any non-Arbitrum parent. It works on EVM chains that support EIP-4844 (e.g. an Ethereum-like L1 used in testing) and is inert elsewhere, since SequencerInbox only calls it for blob batches. This diverges from nitro-contracts' deploy script, which uses the dead address for non-L1 chains and a real reader only on L1.
…Creator The two local factory closures only wrapped deployContractChecked (extracting .address) and sendAndWait (fixing functionName: 'initialize'). Call those shared helpers directly at each site instead.
- deployFullChain now accepts tokenBridgeCreator and weth for a custom parent (it runs the token bridge, not just createRollup). Previously it inherited createRollup's rollupCreator-only schema, so the token-bridge step threw "Address ... missing on custom parent chain" after the rollup was already deployed, leaving a half-deployed chain. - Register a custom parent only when parentChainContracts is supplied. Registering on name/currency alone shadowed a working built-in id with a contract-less entry (custom-first findChain), which then broke the isCustom address lookups. - isRunningOnArbitrum rethrows non-contract errors (transport/RPC); only the ArbSys-absent signal (ContractFunctionZeroDataError) returns false, so a transient failure on an Arbitrum parent no longer silently picks the wrong reader4844. - Add clearCustomParentChains and reset the process-global registry between tests that register. - Extract customParentChainSchemaFields to share the custom-parent input trio across schemas.
…ClientSchema
Replace the customParentChainSchemaFields fragment with a customParentChainPublicClientSchema({...})
builder that returns parentChainPublicClientSchema plus the parameterized registration fields. A
command opts into custom-parent support by using it in place of parentChainPublicClientSchema, which
keeps the parentChain-prefixed fields attached to their rpc/id base and preserves the per-command
required-contract subset. deployFullChain overrides only parentChainContracts (name/currency carry
over from the inherited base).
Deploys the three parent-chain factories a custom parent needs (WETH, RollupCreator, TokenBridgeCreator) in one CLI call, returning their addresses. Amends the custom-parent deployFullChain integration test to set up through it.
Remove the findChain synthesize comment, convert deployWeth's doc to JSDoc, restore the ExpressLaneAuctionInitArgs doc, and replace the deployTokenBridgeCreator prose with a pinned 'viem port of <url>' link.
Slim DeployContext to { client, label } (client typed as WalletClient & PublicActions; the account/chain fields were redundant with the extended client). Build the context inline at each deploy function instead of via a helper, keeping the label so deploy/init error prefixes still name the function.
# Conflicts: # pnpm-lock.yaml # src/createTokenBridge-testHelpers.ts # src/deployExpressLaneAuction.ts # src/deployProxyAdmin.ts # src/index.ts # src/package.json # src/scripting/commands.ts # src/scripting/schemaCoverage.ts # src/scripting/schemas/index.ts # src/scripting/viemTransforms.ts
One chain-id resolver, not two. findChain (custom-first, synthesize-on-miss) is the canonical one; findOrDefineChain only differed by taking an rpcUrl it baked into the synthesized chain's rpcUrls (dead -- every caller supplies an explicit http() transport) and by resolving built-in-first (wrong for the custom-shadows-built-in invariant, harmless only because its two callers never hit that case). Repoint withChildChainSign and runContractCommand at findChain and delete it.
Use JSDoc (/**) for the remaining branch-added exported functions (parentChainContractsSchema, customParentChainPublicClientSchema, clearCustomParentChains, buildBridgeCreatorTemplates, buildSetTemplatesArgs), and trim the verbose @returns/@example blocks and the delegatecall/onlyDelegated prose from deployExpressLaneAuction and deployProxyAdmin.
createSafePrepareTransactionRequest and getClientVersion read chain.rpcUrls.default.http[0] directly, which is empty for synthesized and custom-registered chains, yielding an undefined RPC URL. Prefer publicClient.transport.url (the endpoint the caller connected with), mirroring the existing fallback in publicClientToProvider.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
deployWeth,deployRollupCreator(v3.2), anddeployTokenBridgeCreatorSDK functions (viem ports of the upstream deploy scripts); retire the Docker-based token-bridge-creator helperregisterCustomParentChainfactory addresses optional and resolve registered custom chains first (they win over a same-id built-in)createRollup/createTokenBridgeand the deploy workflowsfindChainsynthesizes a minimal chain for an id not in the built-in list (e.g. a freshly deployed child arbitrum chain) instead of throwing, so commands and workflows can target it