Skip to content

feat: Support custom chains in the CLI#731

Open
yahgwai wants to merge 37 commits into
mainfrom
cli-custom-parent-chain
Open

feat: Support custom chains in the CLI#731
yahgwai wants to merge 37 commits into
mainfrom
cli-custom-parent-chain

Conversation

@yahgwai

@yahgwai yahgwai commented Jul 15, 2026

Copy link
Copy Markdown
Contributor
  • Add deployWeth, deployRollupCreator (v3.2), and deployTokenBridgeCreator SDK functions (viem ports of the upstream deploy scripts); retire the Docker-based token-bridge-creator helper
  • Make registerCustomParentChain factory addresses optional and resolve registered custom chains first (they win over a same-id built-in)
  • CLI: expose the three deploy commands, and register a custom parent chain from JSON in createRollup/createTokenBridge and the deploy workflows
  • Add an end-to-end integration test: deploy the factories on the testnode L2, register it as a custom parent, and deploy a full rollup + token bridge through them
  • Support custom child chains in the CLI: findChain synthesizes 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

yahgwai added 16 commits June 9, 2026 11:53
- 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.
@yahgwai
yahgwai changed the base branch from main to fix-cli-custom-chain-resolution July 15, 2026 14:27
yahgwai added 2 commits July 15, 2026 15:36
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.
@yahgwai yahgwai changed the title Support custom parent chains across the CLI Support custom parent chains in the CLI Jul 16, 2026
yahgwai added 10 commits July 16, 2026 12:26
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).
yahgwai added 4 commits July 16, 2026 14:51
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.
@yahgwai
yahgwai changed the base branch from fix-cli-custom-chain-resolution to main July 16, 2026 14:50
@yahgwai yahgwai changed the title Support custom parent chains in the CLI Support custom chains in the CLI Jul 16, 2026
# 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
@yahgwai yahgwai changed the title Support custom chains in the CLI feat: Support custom chains in the CLI Jul 16, 2026
yahgwai added 4 commits July 16, 2026 16:13
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.
@yahgwai
yahgwai requested a review from spsjvc July 17, 2026 10:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant