refactor(scripts): use dfx --system-canisters instead of manual NNS setup#630
refactor(scripts): use dfx --system-canisters instead of manual NNS setup#630
Conversation
… NNS setup Replace manual installation of system canisters (ICP ledger, ICP index, CMC, Internet Identity) with dfx 0.30.2's --system-canisters flag, which bootstraps them automatically via PocketIC at well-known NNS canister IDs. - Bump dfx from 0.25.0 to 0.30.2 - Remove ~110 lines of install/uninstall/setup functions from orbit script - Add setup_system_canisters_post() for ICP transfer and CMC subnet config - Update CI workflows to use --system-canisters - Remove Python/cbor2/crc32 CI dependencies (only needed for native replica) - Update README and getting-started docs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dfx requires the wasm field for type: "custom" canister definitions, even when the canisters are bootstrapped by --system-canisters and won't be deployed via dfx. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dfx 0.30.2 adds JSDoc comments to generated TypeScript declaration files from candid documentation comments. No structural changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…k_list With --system-canisters, the CMC is configured with the real governance canister, so only it can call set_authorized_subnetwork_list. Use dfx's --sender flag to impersonate the governance canister (supported on PocketIC). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dfx 0.30.2 renamed --sender to --impersonate for canister call. Upgrade icx-asset from 0.21.0 to 0.29.0 for asset canister compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Refactors the local dev/CI bootstrapping flow to rely on dfx start --system-canisters (PocketIC-managed system canisters) instead of manually installing NNS/system canisters, and updates docs/CI accordingly.
Changes:
- Switch local startup commands to
dfx start --system-canistersin docs, scripts, and CI. - Simplify the
orbit --initflow by removing manual system-canister install logic and adding a small post-bootstrap helper (ICP transfer + CMC subnet authorization). - Bump
dfxversion and refresh generated Candid TS types / tool versions.
Reviewed changes
Copilot reviewed 7 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
orbit |
Removes manual NNS/system-canister setup and adds setup_system_canisters_post to finalize PocketIC --system-canisters bootstrap. |
dfx.json |
Drops the cmc canister entry and bumps dfx to 0.30.2. |
.github/workflows/tests.yaml |
Updates CI to start with --system-canisters and removes Python/cbor2/crc32 steps. |
README.md |
Updates local dev instructions to use dfx start --system-canisters. |
docs/src/content/docs/developers/getting-started.md |
Updates the dfx start command to --system-canisters. |
package.json |
Updates e2e deploy script to start dfx with --system-canisters. |
scripts/utils.sh |
Bumps icx-asset install version. |
apps/wallet/src/generated/**.did.d.ts |
Regenerated TS declarations with expanded JSDoc/comments. |
Comments suppressed due to low confidence (1)
docs/src/content/docs/developers/getting-started.md:69
- Step 4 says "Start the Internet Computer replica in the background", but the command shown does not use
--background, and--system-canistersstarts PocketIC rather than the native replica. Also, the subsequentdfx deploywill attempt to deploy all canisters indfx.json(includingicp_ledger/icp_index/internet_identity), which is likely not the intended local-dev flow now that system canisters are bootstrapped automatically. Consider updating this step to mention PocketIC and to usedfx start --background ...plus./orbit --init(as in the root README) instead ofdfx deploy.
## Step 4: Start the Local Development Server
1. Start the Internet Computer replica in the background:
```bash
dfx start --clean --system-canisters --host 127.0.0.1:4943
-
Deploy the canisters to the local replica:
dfx deploy
</details>
---
💡 <a href="/dfinity/orbit/new/main?filename=.github/instructions/*.instructions.md" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Learn how to get started</a>.
| TOPO="$(curl -s http://localhost:4943/_/topology)" | ||
| SUBNET_IDS="$(echo $TOPO | jq -r '.subnet_configs | to_entries | map(select(.value.subnet_kind == "Application"))[] | .key' | sed "s/\(.*\)/principal\\\"\1\\\";/")" |
There was a problem hiding this comment.
setup_system_canisters_post hard-codes http://localhost:4943 and assumes PocketIC is immediately ready. In CI (and locally when dfx start --background was just launched), the topology endpoint may not be available yet, causing curl/jq to fail under set -e. Consider deriving the base URL from get_replica_url, using curl -f (and quoting "$TOPO"), and adding a small retry/wait loop (and/or validating SUBNET_IDS is non-empty) before calling set_authorized_subnetwork_list.
| TOPO="$(curl -s http://localhost:4943/_/topology)" | |
| SUBNET_IDS="$(echo $TOPO | jq -r '.subnet_configs | to_entries | map(select(.value.subnet_kind == "Application"))[] | .key' | sed "s/\(.*\)/principal\\\"\1\\\";/")" | |
| REPLICA_URL="$(get_replica_url)" | |
| TOPOLOGY_URL="${REPLICA_URL%/}/_/topology" | |
| TOPO="" | |
| for _ in {1..10}; do | |
| if TOPO="$(curl -fsS "$TOPOLOGY_URL")"; then | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| if [ -z "$TOPO" ]; then | |
| echo "Failed to fetch topology from $TOPOLOGY_URL" >&2 | |
| return 1 | |
| fi | |
| SUBNET_IDS="$(printf '%s' "$TOPO" | jq -r '.subnet_configs | to_entries | map(select(.value.subnet_kind == "Application"))[] | .key' | sed 's/\(.*\)/principal\"\1\";/')" | |
| if [ -z "$SUBNET_IDS" ]; then | |
| echo "No application subnet IDs found in topology from $TOPOLOGY_URL" >&2 | |
| return 1 | |
| fi |
| function setup_system_canisters_post() { | ||
| # Transfer ICP from anonymous to current user. | ||
| # --system-canisters gives the anonymous identity 1 Billion ICP; | ||
| # the current identity needs ICP for canister operations. | ||
| dfx ledger transfer --identity anonymous \ | ||
| --amount 10000 \ | ||
| "$(dfx ledger account-id)" \ | ||
| --memo 0 | ||
|
|
||
| # Configure CMC authorized subnet list for canister creation. | ||
| # The CMC only allows the governance canister to set authorized subnets, | ||
| # so we use --impersonate to impersonate it (supported on PocketIC). | ||
| TOPO="$(curl -s http://localhost:4943/_/topology)" | ||
| SUBNET_IDS="$(echo $TOPO | jq -r '.subnet_configs | to_entries | map(select(.value.subnet_kind == "Application"))[] | .key' | sed "s/\(.*\)/principal\\\"\1\\\";/")" | ||
| dfx canister call rkp4c-7iaaa-aaaaa-aaaca-cai set_authorized_subnetwork_list "(record { | ||
| who=null; | ||
| subnets=vec {$SUBNET_IDS}; | ||
| }) | ||
| " | ||
| } | ||
|
|
||
| function uninstall_icp_ledger() { | ||
| dfx canister delete icp_ledger -y >/dev/null 2>&1 || true | ||
| } | ||
|
|
||
| function install_icp_ledger() { | ||
| if [ "$MINTER_IDENTITY_NAME" == "$WHOAMI" ]; then | ||
| echo "You can't run this script as the minter identity. Please run it as a different identity." | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! dfx identity list | grep -q $MINTER_IDENTITY_NAME; then | ||
| dfx identity new $MINTER_IDENTITY_NAME --storage-mode plaintext | ||
| fi | ||
|
|
||
| dfx deploy --specified-id $CANISTER_ID_ICP_LEDGER icp_ledger --argument " | ||
| (variant { | ||
| Init = record { | ||
| minting_account = \"$(dfx ledger account-id --identity $MINTER_IDENTITY_NAME)\"; | ||
| initial_values = vec { | ||
| record { | ||
| \"$(dfx ledger account-id)\"; | ||
| record { | ||
| e8s = 1_000_000_000_000 : nat64; | ||
| }; | ||
| }; | ||
| }; | ||
| send_whitelist = vec {}; | ||
| transfer_fee = opt record { | ||
| e8s = 10_000 : nat64; | ||
| }; | ||
| token_symbol = opt \"ICP\"; | ||
| token_name = opt \"Internet Computer\"; | ||
| } | ||
| }) | ||
| " | ||
| } | ||
|
|
||
| function setup_icp_ledger() { | ||
| uninstall_icp_ledger | ||
| install_icp_ledger | ||
| } | ||
|
|
||
| function uninstall_icp_index() { | ||
| dfx canister delete icp_index -y >/dev/null 2>&1 || true | ||
| } | ||
|
|
||
| function install_icp_index() { | ||
| dfx deploy icp_index --specified-id $CANISTER_ID_ICP_INDEX --argument "(record {ledger_id = principal \"$CANISTER_ID_ICP_LEDGER\"})" | ||
| } | ||
|
|
||
| function setup_icp_index() { | ||
| uninstall_icp_index | ||
| install_icp_index | ||
| } | ||
|
|
||
| function uninstall_internet_identity() { | ||
| dfx canister delete internet_identity -y >/dev/null 2>&1 || true | ||
| } | ||
|
|
||
| function install_internet_identity() { | ||
| dfx deploy --specified-id $CANISTER_ID_INTERNET_IDENTITY internet_identity | ||
| } | ||
|
|
||
| function setup_internet_identity() { | ||
| uninstall_internet_identity | ||
| install_internet_identity | ||
| })" --impersonate rrkah-fqaaa-aaaaa-aaaaq-cai |
There was a problem hiding this comment.
This function performs a ledger transfer and a privileged CMC call using well-known mainnet canister IDs, but it doesn't enforce that the active dfx network is local/PocketIC. If a user has DFX_NETWORK set to a non-local network, the script will attempt to interact with real canisters (and --impersonate will fail unpredictably). Consider explicitly passing --network local for these calls, or add a guard that aborts unless the network is local.
| dfx start --clean --system-canisters --host 127.0.0.1:4943 | ||
| ``` | ||
|
|
||
| Note that the local replica should be stopped using `dfx stop` rather than by CTRL^C. |
There was a problem hiding this comment.
This section now instructs starting PocketIC, but the next sentence still refers to "the local replica". Consider updating the wording to "PocketIC" (or "dfx" process) for consistency with the new --system-canisters guidance.
| Note that the local replica should be stopped using `dfx stop` rather than by CTRL^C. | |
| Note that PocketIC should be stopped using `dfx stop` rather than by CTRL^C. |
63337c5 to
410d78d
Compare
The --system-canisters flag deploys a bundled II version whose UI differs from the release-2025-08-29 version the e2e tests expect. Re-deploy the project's pinned II after bootstrap so the captcha-based registration flow is available for e2e tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
410d78d to
d2bd528
Compare
…d frontend The upgrade mode preserves the stable memory and frontend assets from the system-bootstrapped II, which doesn't have the captcha-based registration UI that the e2e tests expect. Using reinstall does a clean install with the project's pinned II wasm. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
dfx start --system-canisters, which bootstraps them automatically via PocketICorbitscript, add ~15-line post-bootstrap helper for ICP transfer and CMC subnet configurationTest plan
deployment-testpasses (PocketIC +--system-canisters)deployment-test-replicapasses (was native replica, now also PocketIC since dfx 0.27.0 removed the native replica)prod-deployment-testpassese2e-testspasseswith_icp_features)dfx ledger transfer --identity anonymoussuccessfully credits ICP to current user🤖 Generated with Claude Code