Shared Boundary Rewrites and Rules
The compiler rewrites shared signatures to transport token values through module-owned wire wrappers. This page is about the boundary where a local module-owned value becomes a network value and then becomes module-owned again on the receiving side.
That boundary has two jobs. First, it prevents callers from manually
constructing or inspecting token payloads that belong to another module.
Second, in proof-enabled IC/Ref builds, sr9 calls carry proof material so the
receiver can reject invalid or non-canister callers before token conversion.
<TokenName>Shared Module and Wire Format
To move token values across canisters, the defining module must provide a public <TokenName>Shared submodule with a private, shareable wire representation:
public module TokenShared {
private type Shared = { /* wire fields */ };
private func fromShared(s : Shared) : Token;
private func toShared(t : Token) : Shared;
};
The compiler rewrites shared signatures so every token position (including
nested inside records, tuples, variants, options, and arrays) is replaced with
the wire wrapper. It inserts fromShared on entry and toShared on return.
toShared should consume the value (burn, zero, or invalidate) so you do not return an asset while keeping a live alias inside the canister. The compiler does not automatically invent the burn; the conversion body and its contracts must establish the intended asset discipline. This is the same ownership concern described in mutable-record aliasing, applied to token values at a shared boundary.
This page is only about shared/network boundaries. Inside verified module code,
opaque/token handles may be stored and routed as sealed identities through
capability-summarized synchronous module APIs, including supported
handle-bearing containers. Local-only opaque handles still do not appear in
public actor/shared signatures; use shareable token wrappers for canister
calls.
Stable upgrade storage is not a public shared/Candid boundary. Source code can
declare stable token/opaque handles, including inside supported containers.
Stable upgrade must not call toShared/fromShared;
those hooks are only for shared-boundary transport and explicit import/export.
For token, stable compatibility is tied to the defining token module CHASH;
if the token module identity changes, that is an explicit migration to a new
token identity, not an automatic stable upgrade. In a multi-actor protocol,
upgrading one actor should therefore make changed token identities
incompatible with actors that have not upgraded yet, unless those actors
explicitly accept the new identity. For local opaque, stable compatibility
is ordinary Motoko raw stable type compatibility; shape changes require an
explicit owner-module migration.
Token Module Rules
These rules are required for shareable token types:
- A module may define at most one
tokentype. - If a module defines
public type Name = token { ... }, it must also define a publicmodule NameSharedwith:- private, non-polymorphic
type Sharedwhose representation is shareable - private, non-async
toShared(t : Name) : Shared - private, non-async
fromShared(s : Shared) : Name - no other items (except a compiler-inserted proof helper)
- private, non-polymorphic
toSharedandfromSharedare untrusted and must verify.- Any shared method that accepts or returns shareable token values must be marked
sr9.
Proofs, Tags, and Call Safety
The compiler inserts a private proof helper inside <TokenName>Shared that enforces a round-trip property:
toShared(fromShared(s)) == s
If the implementations are wrong, verification fails.
Additional rules enforced by the compiler:
- The compiler computes a module hash (CHASH) from the typed AST and tags the wire type as
{ __mod$<hash> : NameShared.Shared }. - In proof-enabled IC/Ref builds, verified shared calls wrap arguments and returns as
{ __proof$ : Blob; __data$ : <wire> }and validate proofs before use. - User code cannot define
__mod$,__proof$, or__data$-prefixed fields or construct the wire wrapper manually. from_candid,to_candid, andcall_raware disabled to prevent bypassing tag checks.- In proof-enabled builds,
sr9rejects non-canister principals (anon, self-auth, derived, reserved) before proof parsing. - The compiler injects controller-only
__s9_set_proofand__s9_set_gate_keymethods to install proof material at runtime.
These rules are enforced before verification. For example, a missing <TokenName>Shared module, a public Shared type, an async toShared, an extra member in <TokenName>Shared, a polymorphic token type, a second token type in the same module, or a token-bearing shared method without sr9 is rejected by the frontend.
sr9 is required for shared signatures that expose shareable token values, and
it is also allowed without token values when you want the shared method to be a
proof-enabled actor boundary. In that form it is useful for protocols where one
Sector9 actor imports another actor and wants downstream calls to carry proof
meaning. The caller still reasons from the callee's contracts and shared
interface; the proof wrapper ties that reasoning to a checked canister
participant at the message boundary.
Design Checklist
Before exposing a token value over a shared method, check each item:
- The token owner module defines the
tokentype and its minimal<TokenName>Sharedmodule. Sharedis private, non-polymorphic, and shareable.toSharedandfromSharedare non-async and verify their intended round trip.- The public shared method is marked
sr9. - The method has explicit entry guards and postconditions for the protocol facts callers rely on.
- Asset identity includes the relevant module hash when spoofing between modules would be dangerous.
Summary
- The compiler rewrites shared signatures to wire types and inserts
toSharedandfromSharedat the boundary. <TokenName>Sharedis required for every shareabletokentype and must be minimal and non-async.sr9methods enforce proof-based authentication in proof-enabled builds, and token wrappers use module-hash tagging for shared-boundary wire types.- Use Digital Asset System Overview for identity and proof flow, then Token Tutorials for concrete patterns.