Skip to content

docs: onchain calls guide#15

Merged
marc0olo merged 7 commits into
mainfrom
docs/guides-canister-calls-onchain-calls
Mar 17, 2026
Merged

docs: onchain calls guide#15
marc0olo merged 7 commits into
mainfrom
docs/guides-canister-calls-onchain-calls

Conversation

@marc0olo
Copy link
Copy Markdown
Member

@marc0olo marc0olo commented Mar 16, 2026

Summary

Comprehensive how-to guide for making inter-canister calls on ICP:

  • Query vs update call comparison (latency, cost, trust model)
  • Making calls in Motoko (canister:name imports, try/catch error handling)
  • Making calls in Rust (Call::unbounded_wait/bounded_wait builder pattern, clean/non-clean reject handling)
  • Canister discovery via icp-cli environment variables (PUBLIC_CANISTER_ID:<name>) as the recommended approach, with init arguments and hardcoded principals as alternatives
  • Bounded vs unbounded wait strategies with upgrade safety and third-party trust considerations
  • Pub/sub pattern with Motoko publisher/subscriber examples
  • Important caveats: 2 MB payload limit, non-atomic execution, caller identity across await, reentrancy, canister_inspect_message scope, cross-subnet latency

Notes:

  • Motoko canister:name import syntax is being redesigned for icp-cli compatibility — flagged with notes throughout
  • No Rust pub/sub example exists upstream — tracked with TODO comment

Sync recommendation

Informed by dfinity/portaldocs/building-apps/interact-with-canisters/advanced-calls.mdx, docs/building-apps/developer-tools/cdks/rust/intercanister.mdx, multi-canister icskill, dfinity/examples motoko/pub-sub, and dfinity/icp-cli docs/concepts/canister-discovery.md

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the placeholder content brief for the onchain calls guide with a comprehensive how-to guide covering inter-canister calls in Motoko and Rust on ICP.

Changes:

  • Added a complete guide covering query vs update calls, making calls in Motoko (canister:name imports) and Rust (Call::unbounded_wait/bounded_wait builder pattern), error handling, bounded vs unbounded wait strategies, and important caveats (payload limits, non-atomic execution, caller identity, reentrancy, canister_inspect_message scope, cross-subnet latency).
  • Included cross-links to related guides (parallel calls, Candid, certified variables, inter-canister call security).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread docs/guides/canister-calls/onchain-calls.md Outdated
@marc0olo
Copy link
Copy Markdown
Member Author

Review: Onchain Calls

Must fix

  • CallErrorExt unused import in "Basic call" example: The basic call example imports CallErrorExt but only uses .expect() (a standard Result method). CallErrorExt is only needed in the error handling example where .is_clean_reject() is called. Remove CallErrorExt from the basic call import.

  • Import path: ic_cdk_macros::update vs ic_cdk::update: The examples import use ic_cdk_macros::update; but ic-cdk re-exports macros at the top level. The conventional import is use ic_cdk::update; (the multi-canister icskill uses use ic_cdk::{update, query};). Use the re-export for consistency with the rest of the docs. Applies to all Rust code blocks.

  • Content brief gap — canister discovery: The stub brief explicitly asks for "canister discovery (env vars, ic_env cookie)" but this isn't covered. At minimum, add a short section or paragraph explaining how a canister learns the Principal of the canister it wants to call (hardcoded, init arg, environment). If better suited for a separate page, mention it and link to a stub.

Suggestions

  • Content brief gap — calling third-party canisters: The brief mentions this. The bounded-wait section partially covers it ("canisters you do not control"), but it could be made more explicit with a paragraph about trust considerations when calling canisters outside your control (bounded wait + idempotency + checking outcomes).

  • Content brief gap — pub/sub pattern: Mentioned in the brief and source material examples. Could reasonably be deferred to a separate page. If omitted, note it in the PR description so it's tracked.

  • Bounded wait timeout example: .change_timeout(1) uses a 1-second timeout, which is very aggressive. Consider a more realistic value (e.g., 5 or 10) or add a comment explaining the unit and reasonable range.

  • "Caller identity across await" Rust example: The example shows let caller = ic_cdk::api::msg_caller(); but doesn't actually use caller after the await — it ends with Ok(()). A more instructive example would show caller being used after an await point.

Verified

  • All 5 internal links resolve to existing files (../../concepts/canisters.md, ../backends/certified-variables.md, parallel-calls.md, candid.md, ../security/inter-canister-calls.md)
  • Rust CDK API verified against .sources/cdk-rs/ic-cdk/src/call.rs: Call::unbounded_wait, Call::bounded_wait, .with_arg(), .candid::<T>(), .candid_tuple(), CallErrorExt::is_clean_reject(), .change_timeout() all exist
  • Motoko syntax verified against .sources/motoko/ and .sources/motoko-core/: persistent actor, import X "canister:name", mo:core/Error with Error.message(), mo:core/Result with Result.Result<Ok, Err>
  • Technical claims cross-checked against multi-canister icskill: 2 MB payload limit, same-subnet single-round latency, cross-subnet 2-3 rounds, clean/non-clean reject semantics, unbounded wait upgrade risk — all accurate
  • No dfx references, no .mdx/JSX, plain markdown only
  • Frontmatter complete and consistent
  • Page follows Diataxis "how-to guide" format correctly
  • Upstream attribution comment present

- Remove unused CallErrorExt import from basic call example
- Use ic_cdk::update re-export instead of ic_cdk_macros::update
- Add canister discovery section (Motoko import, Rust init arg, hardcoded)
- Add pub/sub pattern section with Motoko publisher/subscriber examples
- Add third-party canister calling guidance in bounded wait section
- Use realistic 5s timeout in bounded wait example
- Make caller identity example instructive (use caller after await)
- Add TODO for Rust pub/sub example in dfinity/examples
@marc0olo
Copy link
Copy Markdown
Member Author

Feedback addressed:

  • Removed unused CallErrorExt import from basic call example
  • Changed ic_cdk_macros::updateic_cdk::update re-export across all Rust examples
  • Added "Canister discovery" section covering Motoko compile-time import, Rust init argument, and hardcoded principals
  • Added "Pub/sub pattern" section with Motoko publisher/subscriber examples (adapted from dfinity/examples/motoko/pub-sub). Added TODO comment for creating a Rust pub/sub example upstream.
  • Added third-party canister calling guidance (bounded wait + idempotency + outcome querying)
  • Changed .change_timeout(1).change_timeout(5) with unit comment
  • Made caller identity example instructive — now shows caller used after an await point
  • Pub/sub pattern deferred to separate page: no, included inline as a section (natural fit for inter-canister calls guide). Noted in PR description.

… redesign

- Rewrite canister discovery section to recommend icp-cli's automatic
  PUBLIC_CANISTER_ID:<name> environment variables as the primary approach
- Show Rust (ic_cdk::api::env_var_value) and Motoko (Prim.envVar) patterns
- Move init arguments and hardcoded principals to "Alternative approaches"
- Add notes throughout about canister:name syntax being redesigned for
  icp-cli compatibility
- Add note on pub/sub subscriber's canister:pub import
@marc0olo
Copy link
Copy Markdown
Member Author

Feedback addressed (follow-up):

  • Rewrote canister discovery section to recommend icp-cli's automatic PUBLIC_CANISTER_ID:<name> environment variables as the primary approach (informed by icp-cli/docs/concepts/canister-discovery.md)
  • Added Rust (ic_cdk::api::env_var_value) and Motoko (Prim.envVar) code examples for reading env vars
  • Moved init arguments and hardcoded principals to "Alternative approaches" subsection
  • Added notes throughout about Motoko canister:name syntax being redesigned for icp-cli compatibility
  • Added note on pub/sub subscriber's canister:pub import pointing to the canister discovery section

…eam notes

- Replace Prim.envVar (internal mo:⛔ module) with Runtime.envVar from
  mo:core/Runtime — the public API available since motoko-core v2.1.0
- Add icp-cli docs and icp-cli icskill to upstream attribution comment
- Update pub/sub publisher example from mo:base/List (linked list) to
  mo:core/List (mutable growable array) for consistency with other examples
- Fix caller identity example: pass ledger as parameter instead of
  using undefined LEDGER constant
@marc0olo marc0olo merged commit 31d9ca6 into main Mar 17, 2026
1 check passed
@marc0olo marc0olo deleted the docs/guides-canister-calls-onchain-calls branch March 17, 2026 09:34
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.

2 participants