refactor(motoko/icrc2-swap): generate ICRC bindings from a committed .did - #1454
Open
marc0olo wants to merge 1 commit into
Open
refactor(motoko/icrc2-swap): generate ICRC bindings from a committed .did#1454marc0olo wants to merge 1 commit into
marc0olo wants to merge 1 commit into
Conversation
….did
Replace the hand-written `backend/ICRC.mo` type module with Motoko bindings
generated by `didc` from a committed ICRC-1/ICRC-2 interface, demonstrating
the pattern for a backend that calls *many* ledgers with the ledger chosen at
runtime — the complement to the single-fixed-target `--actor-env-alias`
examples.
- candid/icrc.did — the ICRC-1/ICRC-2 interface the backend calls
(types taken from the ic-icrc1-ledger reference wasm,
as a focused plain service). Regen source.
- bindings/ICRC.mo — `didc bind -t mo candid/icrc.did`; committed so
building needs no extra tooling.
- backend/app.mo — import the generated bindings; `actor(<token>) : ICRC.Self`
keeps the runtime-chosen-principal pattern.
- removed backend/ICRC.mo (hand-written types).
Also, unrelated tidy-ups in the same file/example:
- Map `swap` (return value always discarded) -> `add` (`ignore swap`), 6 sites.
- bump mo:core 2.5.0 -> 2.6.0.
Verified end-to-end: `bash deploy.sh && bash test.sh` — all 7 tests pass
(deposit via icrc2_transfer_from, atomic swap, withdraw via icrc1_transfer),
confirming the generated bindings are wire-compatible with the real ledger.
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/icrc2-swap example to use a committed, didc-generated Motoko binding for the standard ICRC-1/ICRC-2 ledger interface, allowing the backend to type ledgers selected by principal at runtime (actor(<principal>) : ICRC.Self) without maintaining a hand-written interface module.
Changes:
- Adds a focused
candid/icrc.didfor the ICRC-1/ICRC-2 method subset used by the example and commits the generated Motoko binding inbindings/ICRC.mo. - Updates the backend to import and use
ICRC.Selffrom the generated bindings, while preserving the runtime-selected-ledger pattern. - Bumps
mo:corefrom2.5.0to2.6.0and applies minor map-update tidy-ups (swap→add).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| motoko/icrc2-swap/README.md | Updates architecture docs and adds binding-regeneration instructions using didc. |
| motoko/icrc2-swap/mops.toml | Bumps mo:core dependency version. |
| motoko/icrc2-swap/candid/icrc.did | Introduces the committed Candid interface used as the binding source-of-truth. |
| motoko/icrc2-swap/bindings/ICRC.mo | Adds committed, generated Motoko service/types binding (ICRC.Self, records, variants). |
| motoko/icrc2-swap/backend/app.mo | Switches backend to the generated binding import and updates types/usages accordingly. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Contributor
|
Yes, I think once dfinity/candid#729 lands it would make sense for mops to adopt this tool. |
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
Replaces the hand-written
backend/ICRC.motype module inicrc2-swapwith Motoko bindings generated bydidcfrom a committed ICRC-1/ICRC-2.did. This example is the deliberate counterpart to the single-fixed-target--actor-env-aliasexamples (ic-pos,evm_block_explorer,llm_chatbot): a backend that talks to many ledgers, with the ledger chosen at runtime.Why this pattern for ICRC
icrc2-swapholds two ledgers at once (token_a,token_b) and picks the target per call (actor(args.token.toText()) : ICRC.Self). That's the "one interface, many ledgers, runtime-chosen principal" case — which--actor-env-aliasstructurally cannot express (it binds one import to one principal). The right tool is a shared generated service type +actor(id). This generalizes to any DeFi-shaped backend (swaps, routers, wallets) that must interact with many ICRC ledgers (ICP, ckBTC/ckETH, SNS tokens, …).Contrast with
ic-pos, which talks to a single configured ledger and also calls a ledger-specific method (get_transactions) — there--actor-env-aliaswith the full ledger.didis the right fit. The two examples now demonstrate both axes on purpose.Changes
candid/icrc.did— the ICRC-1/ICRC-2 interface the backend calls, as a focused plain service. Type definitions are taken from theic-icrc1-ledgerreference wasm (the exact artifact this example pins fortoken_a/token_b); the service is the standard ICRC-1 + ICRC-2 method subset. This is the regeneration source.bindings/ICRC.mo—didc bind -t mo candid/icrc.did. Committed, so building needs no extra tooling.backend/app.mo— imports the generated bindings (ICRC.Self), keeps the runtime-principal pattern; hand-writtenICRC.moremoved.swap(return always discarded) →add(which is literallyignore swap), 6 sites; andmo:core2.5.0 → 2.6.0.Testing
bash deploy.sh && bash test.sh— all 7 tests pass (deposit viaicrc2_transfer_from, atomic swap, withdraw viaicrc1_transferwith correct fee-adjusted balance deltas), confirming the generated bindings are wire-compatible with the real ledger.Discussion: binding generation & tooling (cc @Kamirus)
The bindings here are idiomatic straight from
didctoday because ICRC-1/2 Candid type names are already PascalCase. For interfaces whose Candid type names are snake_case (e.g. an LLM canister'schat_message_v1), that isn't the case — which is exactly what dfinity/candid#729 fixes (PascalCase Motoko type aliases). Flagging it because it's the generator that makes this pattern broadly clean.Bigger picture: we commit
bindings/ICRC.mohere because the only generator today isdidc, a separate tool outside theicp-cli/mops/ic-wasmtoolchain — gitignoring it would force adidcinstall on everyone who builds. The ideal end state would mirror the JS/TS story (@icp-sdk/bindgengeneratessrc/bindings/*.tsat build time, gitignored):mopsgenerating.did→Motoko bindings as a pre-build step, with the.didcommitted and the generated.mogitignored (ideally with committing as an opt-in for teams that prefer it).mopsalready does the forward direction (mops generate candid), so this is the symmetric missing half — and #729 is the generator that would power it. Until then, committing the bindings is the right call.(
didcreleases: https://github.com/dfinity/candid/releases)🤖 Generated with Claude Code