multi-canister

Design and deploy multi-canister ICP dapps with inter-canister calls and factory patterns.

Updated Apr 3, 2026
One-click install
npx skills add https://github.com/phukrit7171/Relationship-Smart-Contract-ICP --skill multi-canister-phukrit7171
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: multi-canister
Source: https://github.com/phukrit7171/Relationship-Smart-Contract-ICP/tree/main/.agents/skills/multi-canister
Command: npx skills add https://github.com/phukrit7171/Relationship-Smart-Contract-ICP --skill multi-canister-phukrit7171

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? Splitting an Internet Computer application across multiple canisters introduces subtle failure modes: non-atomic update calls, 2MB payload limits, reentrancy risks, and unbounded waits that can block upgrades. This Skill guides the design and implementation of multi-canister architectures in Motoko and Rust while avoiding these functional and security bugs. ## Core Features & Use Cases - Inter-Canister Call Patterns: Implement bounded vs unbounded wait calls, handle rejections, capture callers before await, and design idempotent APIs with outcome queries. - Canister Factory Pattern: Dynamically create and install child canisters with cycle management and stable-memory tracking in both Motoko and Rust. - Pitfall Prevention: Covers reentrancy, traps in callbacks, cross-subnet latency and bandwidth, shared type divergence, and deployment ordering via icp.yaml dependencies. - Use Case: You are building a dapp whose data exceeds a single canister's heap, so you split it into a user service and a content service that validates users via inter-canister calls, with shared Candid types and stable storage that survives upgrades. ## Quick Start Use the multi-canister skill to split my ICP app into a user service and a content service canister that communicate via inter-canister calls.

Frequently Asked Questions about multi-canister

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I make inter-canister calls between ICP canisters?

In Motoko, import the target canister with `import Service "canister:name"` and await its shared methods inside try/catch. In Rust, use `Call::unbounded_wait(canister_id, "method")` from ic-cdk and handle the returned Result to catch rejections.

When should I split my ICP app into multiple canisters?

Split when data exceeds single-canister heap limits, when you need to shard compute across subnets, or when teams need independent upgrade cycles and separate controllers. Simple apps under 1GB of data should stay single-canister to avoid inter-canister call overhead.

What is the difference between bounded and unbounded wait calls on ICP?

Unbounded wait guarantees the caller learns the call outcome but may block upgrades indefinitely if the callee never responds. Bounded wait may return a SYS_UNKNOWN response after a timeout, so non-idempotent calls need a separate endpoint to query the outcome.

Why does my inter-canister call fail with large payloads?

Canister request and response payloads are limited to 2MB because cross-subnet messages are packed into 4MB blocks. Requests above 2MB fail synchronously and oversized responses trap, so chunk large transfers into 1MB pieces across multiple calls.

How do I create canisters dynamically from another canister?

Use the canister factory pattern: call the management canister's create_canister with attached cycles (around 1T), then install_code with the wasm module and init arguments. Track created canisters in stable memory so the mapping survives upgrades.

Why is msg_caller wrong after an await in Rust canisters?

Code before and after an await runs as separate message executions, so relying on caller context across the await is fragile. Capture `ic_cdk::api::msg_caller()` into a local variable before the await and use that binding afterward.