refactor(motoko): canister: import + --actor-env-alias for inter-canister calls (pub-sub, parallel_calls) - #1452
Merged
Merged
Conversation
…canister calls Migrate the two intra-project Motoko examples that make inter-canister calls to the typed `canister:<name>` import backed by moc's `--actor-env-alias` flag, instead of hand-written actor types plus `Runtime.envVar` + `actor(id)`. For each caller: - import the target by name (`import Callee "canister:callee"`, `import Publisher "canister:publisher"`); - commit the target's Candid interface (`callee/callee.did`, `publisher/publisher.did`), generated via `mops generate candid <name>`; - wire the import in mops.toml with `--actor-env-alias <alias> PUBLIC_CANISTER_ID:<name> <did-path>` (per-canister args replace the global [moc].args, so the global flags are repeated), and add `candid = ...` to the target canister so its emitted interface is checked against the committed .did. The .did now supplies the interface, so no actor type is declared in Motoko. The principal is still resolved from PUBLIC_CANISTER_ID:<name>, but at install/upgrade time from the canister environment variable — no principal is compiled into the Wasm, so the same artifact runs in every environment. READMEs gain a "How the <target> is resolved" section describing the build/deploy/runtime split and an "Updating the Candid interface" note pointing to `mops generate candid <name>`. Verified: both examples build with `mops build`; deployed locally and `test.sh` passes for pub-sub and the sequential/parallel calls succeed for parallel_calls. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…aller in PocketIC test
The multi-subnet test set PUBLIC_CANISTER_ID:callee after installing the
caller. That worked while the caller read the variable lazily per call via
Runtime.envVar, but with the `canister:callee` import + --actor-env-alias the
variable is bound at canister init — installing the caller with it unset traps
("envvar `PUBLIC_CANISTER_ID:callee` not set").
Create both canisters first, install the callee, set the caller's environment
variable, then install the caller. Verified locally: 90/90 sequential and
90/90 parallel calls succeed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR refactors the Motoko pub-sub and parallel_calls examples to use typed canister:<name> imports wired via moc --actor-env-alias, replacing manual Runtime.envVar + actor(id) plumbing and committing the target canisters’ Candid interfaces for compile-time typing.
Changes:
- Migrated inter-canister references to
import <X> "canister:<x>"in the call-site canisters (subscriber,caller). - Added committed
.didfiles for the target canisters and configuredmops.tomlto type imports via--actor-env-aliasand to check emitted Candid against the committed.did. - Updated READMEs and the
multi_subnetPocketIC harness to reflect the new “resolve-at-init from env var” binding model.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| motoko/pub-sub/subscriber/app.mo | Replaces runtime actor construction with typed canister:publisher import and direct calls. |
| motoko/pub-sub/README.md | Documents the new typed import + --actor-env-alias resolution model and Candid regeneration command. |
| motoko/pub-sub/publisher/publisher.did | Adds committed publisher Candid interface used to type the subscriber’s import. |
| motoko/pub-sub/mops.toml | Adds candid = ... for publisher and per-canister args for subscriber with --actor-env-alias. |
| motoko/parallel_calls/README.md | Documents the new typed import + --actor-env-alias resolution model and Candid regeneration command. |
| motoko/parallel_calls/multi_subnet/src/main.rs | Adjusts PocketIC install/settings order so env var is set before caller install/init binding. |
| motoko/parallel_calls/mops.toml | Adds per-canister args for caller with --actor-env-alias and candid = ... for callee. |
| motoko/parallel_calls/caller/app.mo | Replaces runtime actor construction with typed canister:callee import and direct calls. |
| motoko/parallel_calls/callee/callee.did | Adds committed callee Candid interface used to type the caller’s import. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…l-flow diagram `subscriber.updateCount` returns () (Candid `func -> () oneway`), so it is a fire-and-forget message with no awaitable reply. Labeling it "async callback" conflated asynchronous message delivery with an async return type and contradicted the code comment. Address Copilot review feedback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
raymondk
approved these changes
Jul 27, 2026
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.
What
Migrates the two intra-project Motoko examples that make inter-canister calls —
parallel_callsandpub-sub— to the typedcanister:<name>import backed by moc's--actor-env-aliasflag, replacing hand-written actor types +Runtime.envVar+actor(id).This is the pattern we want to advertise going forward: the target canister's
.didsupplies the interface (no actor type in Motoko), while the principal stays runtime-resolved fromPUBLIC_CANISTER_ID:<name>.Changes
For each caller (
caller→callee,subscriber→publisher):import Callee "canister:callee"/import Publisher "canister:publisher". The hand-writtenCalleeActor/PublisherActortypes and theRuntime.envVar+actor(id)helpers are gone.callee/callee.did,publisher/publisher.did, generated viamops generate candid <name>.mops.toml—--actor-env-alias <alias> PUBLIC_CANISTER_ID:<name> <did-path>on the caller. Per-canisterargsreplace the global[moc].args, so the global flags (--default-persistent-actors,-W=...) are repeated. Addedcandid = ...on the target canister so its emitted interface is checked against the committed.did.mops generate candid <name>(matches PR docs(motoko): align Candid regeneration tomops generate candid backend#1450's convention).Build-time vs runtime (verified against the compiled Wasm)
moc): the.didgives the import its static type; the Wasm embeds only the env-var name + the IC env-var syscalls (ic0.env_var_*) — no principal. The build is told no principal and no target environment, so the same Wasm artifact runs in every environment.icp-cli): sets the canister env varPUBLIC_CANISTER_ID:<name>to the target's principal — auto-injected for project canisters, overridable per-environment inicp.yaml.canister_init/canister_post_upgrade/destabilization, not the update methods), then reuses the principal.Testing
mops buildsucceeds for both examples, including against the regenerated.didfiles.pub-sub'stest.shpasses all 4 tests (registration + async callback delivery);parallel_callssequential/parallel calls succeed.🤖 Generated with Claude Code