Skip to content

feat(motoko): EncryptedMaps canister mixin for one-line canister integration#405

Draft
marc0olo wants to merge 2 commits into
mainfrom
dx/motoko-canister-mixin
Draft

feat(motoko): EncryptedMaps canister mixin for one-line canister integration#405
marc0olo wants to merge 2 commits into
mainfrom
dx/motoko-canister-mixin

Conversation

@marc0olo

Copy link
Copy Markdown
Member

Draft — third of three PRs for #402 (DX: canister-integration helpers).

What

Uses Motoko's new actor mixins (moc 1.9.0) so the ic-vetkeys library can provide the entire EncryptedMaps canister interface, and refactors the reference canister to include it:

import EncryptedMapsCanister "mo:ic-vetkeys/encrypted_maps/Canister";

persistent actor class (keyName : Text) {
    include EncryptedMapsCanister(keyName, "my_domain_separator");
};

This replaces ~200 lines of hand-written delegation (one shared/query func per library method, ByteBufBlob wrapping). It's the Motoko counterpart to the Rust macro in #404.

Why

  • Interface correct by construction: the mixin is the single source of the endpoint set, so the Candid matches the @icp-sdk/vetkeys frontend exactly.
  • Upgrade-safe: the mixin owns its stable state; included into a persistent actor the maps survive upgrades.

Verification (local)

  • mops build compiles the mixin-based canister (moc 1.9.0 + local lib).
  • All 18 existing canister integration tests pass against the mixin-generated Motoko wasm (make test — the repo's Rust integration suite run against the Motoko canister).
  • The generated .did exposes the same method set as the canonical Rust .did.
  • mops format clean.

Toolchain changes

  • Bump moc to 1.9.0 (mixins) in the library + canister mops.toml.
  • Canister icp.yaml recipe → @dfinity/motoko@v5.0.0, which delegates the build to mops (entry point + moc come from mops.toml).
  • Use core's Array.map (the current library returns plain arrays, which have no .map method under moc 1.9.0).

⚠️ Draft / publish-bootstrapping notes

  • Local dependency: to dogfood the unreleased mixin, the reference canister temporarily depends on the library via a local path (ic-vetkeys = "../../ic_vetkeys"). On release this flips to the published ic-vetkeys@0.6.0.
  • mops.lock not committed for the canister: mops records a machine-specific absolute path for local deps, so committing it would break other machines/CI. CI regenerates it from the relative path in mops.toml. (The Rust macro PR avoids this because Cargo workspace deps are relative.)
  • moc 1.9.0 requirement: mixins raise the minimum moc for consumers — intended, will be documented in the release notes.

Sequencing

A (interface-sync guard, #403) → B (Rust macro, #404) → C (this).

🤖 Generated with Claude Code

marc0olo and others added 2 commits June 16, 2026 19:48
Part of #402. Motoko gained actor mixins (moc 1.9.0), which let a library
provide `public shared`/`query` endpoints that an actor pulls in with
`include`. This adds a mixin that provides the whole EncryptedMaps canister
interface, so an adopter's Main.mo is a few lines instead of ~200:

    import EncryptedMapsCanister "mo:ic-vetkeys/encrypted_maps/Canister";
    persistent actor class (keyName : Text) {
        include EncryptedMapsCanister(keyName, "my_domain_separator");
    };

Because the mixin is the single source of the endpoint set, the exposed
Candid matches what the @icp-sdk/vetkeys frontend expects, by construction.
The mixin owns its stable state, so included into a `persistent actor` the
maps survive upgrades.

- New mixin: backend/mo/ic_vetkeys/src/encrypted_maps/Canister.mo.
- Reference canister reduced to the `include` (dogfooding).
- Bump moc to 1.9.0 (mixins) in the library and canister mops.toml.
- Switch the canister icp.yaml recipe to @dfinity/motoko@v5.0.0, which
  delegates the build to mops (reads entry point + moc from mops.toml).
- Use core's Array.map (the local library returns plain arrays, which have
  no `.map` method under moc 1.9.0).

Verified locally:
- mops build compiles the mixin-based canister.
- All 18 existing canister integration tests pass against the
  mixin-generated Motoko wasm.
- The generated .did exposes the same method set as the canonical Rust .did.
- mops format clean.

Draft note: to dogfood the unreleased mixin the canister uses a temporary
local path dependency on the library; on release this flips to the published
`ic-vetkeys@0.6.0`. mops.lock is intentionally not committed for this change
because mops records a machine-specific absolute path for local deps; CI
regenerates it from the relative path in mops.toml.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CI's `mops install` does a hard integrity check and fails on a
mops.toml/lock hash mismatch (it does not auto-regenerate). Commit a lock
that matches the local-dependency mops.toml, but with the dependency path
rewritten from mops's machine-specific absolute path to the relative
"../../ic_vetkeys" so it resolves on any checkout. Verified: mops install
leaves it intact, and build + format-check pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant