Start
Install the locked dependency tree once, then launch the checkout-local application in a real terminal.
npm ci
./symmio
The operator interface is intentionally menu-only. Arguments and non-TTY execution are refused. Low-level Hardhat tasks remain internal adapters owned by registered tasks.
- Deploy a contract
- Patch configurations for deployed contracts
- Run the checklist on a new deployment
- Other maintenance scripts
- Continue active task
- Cancel active task
- Exit
Guided configuration
Choose a target and network. The form loads reviewed defaults, asks only required decisions and overrides, validates each typed value immediately, then shows public intent grouped by network, governance, Core, Muon, protocol, components, and execution.
After final approval the application writes a portable recipe atomically. Operators review that artifact; they do not need to manually maintain its JSON. Local and fork workflows may deliberately use environment references, but live workflows may not.
One durable task
A live deployment is one connected workflow. It automatically performs:
- recipe, RPC, signer, authority, and dependency preflight;
- production compilation;
- a matching fork rehearsal and receipt review;
- typed live-network confirmation;
- write-ahead transaction journaling and receipt reconciliation;
- explorer verification and current health checks; and
- ownership, role, deployer-privilege, and handover proof.
A completed report is historical evidence, never an active task. The active record is bound to task version, source and input hashes, recipe digest, chain, signer set, and stable plan; changed intent cannot be resumed accidentally.
Progress and recovery
The live view shows phase, completed and total steps, current action, confirmed/pending/failed transaction counts, elapsed
time, and warnings. Press d for hashes, receipts, gas, explorer links, and recent redacted logs.
The first Ctrl+C requests a cooperative pause at the next safe boundary. A second Ctrl+C exits immediately after preserving resumable state. Choose Continue active task to reconcile recorded broadcasts before doing new work.
Cancel active task means safe abandonment, never rollback. Confirmed on-chain effects remain recorded.
Unknown broadcast outcomes hold the task in cancel_pending until reconciliation can prove them.
Handover and external actions
Fresh configuration is completed under temporary deployer roles before those roles are removed, so a normal fresh handover
contains ownership acceptance only. The task then classifies the administrator from chain state. An EOA can use a Ledger
hardware wallet, Hardhat keystore wallet, transient private key, or an allowed unlocked local account. Safe Transaction
Builder and direct proposal choices appear only for an address that passes Safe interface checks; unknown contracts remain
manual. The task displays Handover required until exact post-state reads pass. After external actions
confirm, launch ./symmio and choose Continue active task. The task rereads chain state, skips
completed deployment steps, and closes only after the strict health gate passes.
Replacing the InstantLayer
The InstantLayer is a plain contract, so a code change means a new address.
scripts/migrateInstantLayer.ts carries a live deployment across that change in two phases. The deployer phase
reads the old layer from chain (AccountLayer, whitelisted targets, registered PartyBs, templates in id order, cooldown,
transient flag, and every role holder), deploys and configures the replacement to match, deploys a GaslessLayer implementation
that exposes setInstantLayer, grants all four roles to the Safe, and renounces the deployer. Every step checks
chain state first, so an interrupted run is resumed by running the same command again with the state file it wrote under
scripts/output/.
The admin phase is nine binding transactions: grant INSTANT_LAYER_ROLE on core and
SIGNER_SETTER_ROLE on the AccountLayer to the new layer, trust and multicast-whitelist it on every registered
SymmioPartyB, and upgrade the GaslessLayer with setInstantLayer as the init data; later, the matching revokes for
the old layer. When the governance admin is an EOA whose key is configured as a second signer, the script executes the cutover
itself right after the deployer phase, so one command completes the migration. A Ledger admin signs the same actions through
ADMIN_SIGNER=cast-ledger (Foundry cast send --ledger, as in the governance handover) in a second
command, PHASE=bind. When the admin is a Safe, the actions are written as Transaction Builder batches next to the
state file. The old layer keeps its bindings, so users can keep trading on existing delegations while they re-grant on the new
address; PHASE=decommission revokes the old layer once that window closes. Delegations, pending revocations, and
delegation nonces do not carry over; frontends and relayers must switch the address and the EIP-712
verifyingContract.
# plan (read-only)
npx hardhat run --no-compile scripts/migrateInstantLayer.ts --network arbitrum
# deployer phase (keystore deployer); the cutover runs in the same command when the admin key is a configured signer
EXECUTE=true CONFIRM_CHAIN_ID=42161 npx hardhat run --no-compile scripts/migrateInstantLayer.ts --network arbitrum
# admin phase for a Ledger admin: five cast --ledger transactions, one device confirmation each
EXECUTE=true CONFIRM_CHAIN_ID=42161 PHASE=bind ADMIN_SIGNER=cast-ledger npx hardhat run --no-compile scripts/migrateInstantLayer.ts --network arbitrum
# confirm at any time
CHECK_INSTANT_LAYER=0x<new> npx hardhat run --no-compile scripts/migrateInstantLayer.ts --network arbitrum
# after the transition window
EXECUTE=true CONFIRM_CHAIN_ID=42161 PHASE=decommission ADMIN_SIGNER=cast-ledger npx hardhat run --no-compile scripts/migrateInstantLayer.ts --network arbitrum
Defaults come from tasks/data/<chainId>/deployment-report.json and gaslesslayer.json;
templates are replayed from the old layer with their ids and then extended with the recipe's extra templates
(TEMPLATES_RECIPE, the Arbitrum production recipe by default), whose leading entries must match the deployed ones
exactly; the GaslessLayer implementation links against the library addresses recorded there. Once the cutover is confirmed,
update addresses.instantLayer in that report and append the new entry to instantlayer.json, so
check:deployment and verify:all follow the new layer. The rehearsal in
test/InstantLayerMigration.behavior.ts runs the whole sequence, Safe batches included, against the local full
stack, and scripts/rehearseInstantLayerMigration.ts repeats it on a fork of the live chain with the Safe
impersonated (--network fork-arbitrum with the upstream RPC exported), which is the step to run before spending
real gas.
Evidence
| Path | Purpose |
|---|---|
deployment-recipes/<name>.json |
Portable, reviewed public intent |
.symmio/tasks/active.json |
Single transient active task |
.symmio/tasks/runs/ |
NDJSON event journal and redacted raw log |
.symmio/tasks/history/ |
Indefinite local terminal history |
tasks/data/<chainId>/ |
Ignored local evidence: reports, receipts, Safe exports, and checklists |
tasks/data/<chainId>/deployment-summary.md |
Human-readable important-address handoff; deployment-report.json remains machine evidence |
tasks/data/ is ignored local evidence and is never pushed by Git. Back it up securely when off-machine recovery
is required.
For persistent local-node rehearsal, patch semantics, checklist coverage, and maintenance policies, use the complete deployment runbook. For adding a one-time script, use the task-definition and registry standard.