From feb92bbe20bf1b9c3970ae4b51ac8be7bcaa9dde Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 17 Apr 2026 13:18:20 +0200 Subject: [PATCH 1/8] chore(sources): add chain-fusion-signer, papi, ic-pub-key submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds three release-pinned submodules to support the chain-fusion signer guide (page proposal #24): - chain-fusion-signer v0.3.0 — signer canister API and key derivation - papi v0.1.1 — ICRC-2 cycle payment interface - ic-pub-key v1.0.1 — CLI tool for address/key derivation Updates CLAUDE.md submodule table and bump checklist, VERSIONS, and decisions.md. --- .docs-plan/decisions.md | 5 +++++ .gitmodules | 9 +++++++++ .sources/VERSIONS | 3 +++ .sources/chain-fusion-signer | 1 + .sources/ic-pub-key | 1 + .sources/papi | 1 + AGENTS.md | 6 ++++++ 7 files changed, 26 insertions(+) create mode 160000 .sources/chain-fusion-signer create mode 160000 .sources/ic-pub-key create mode 160000 .sources/papi diff --git a/.docs-plan/decisions.md b/.docs-plan/decisions.md index ebd6e29c..2b271825 100644 --- a/.docs-plan/decisions.md +++ b/.docs-plan/decisions.md @@ -4,6 +4,11 @@ Record decisions that constrain future work — things an agent needs to know th --- +## 2026-04-17: Added chain-fusion-signer, papi, ic-pub-key submodules +**Context:** Page proposal #24 (chain-fusion signer guide) required source material not yet in `.sources/` +**Decision:** Added three new release-pinned submodules: `chain-fusion-signer` (v0.3.0), `papi` (v0.1.1), `ic-pub-key` (v1.0.1) +**Rationale:** All three are needed to write accurate code examples — signer API, ICRC-2 payment setup, and CLI usage respectively. All follow "track latest release" strategy. + ## 2026-03-11: Diataxis framework with 5 top-level sections **Context:** Need a clear information architecture for the new developer docs **Decision:** Use Diataxis with 5 sections: Getting Started (tutorials), Guides (how-to), Concepts (explanations), Languages (synced + hand-written), Reference (information) diff --git a/.gitmodules b/.gitmodules index 4f98c36f..e9eab72d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -43,3 +43,12 @@ [submodule ".sources/dotskills"] path = .sources/dotskills url = git@github.com:vincentkoc/dotskills.git +[submodule ".sources/chain-fusion-signer"] + path = .sources/chain-fusion-signer + url = git@github.com:dfinity/chain-fusion-signer.git +[submodule ".sources/papi"] + path = .sources/papi + url = git@github.com:dfinity/papi.git +[submodule ".sources/ic-pub-key"] + path = .sources/ic-pub-key + url = git@github.com:dfinity/ic-pub-key.git diff --git a/.sources/VERSIONS b/.sources/VERSIONS index 73b34724..d5df1621 100644 --- a/.sources/VERSIONS +++ b/.sources/VERSIONS @@ -46,6 +46,9 @@ # instead. This is expected behaviour with --depth 1 submodule init. # ------------------------------------------------------- +chain-fusion-signer v0.3.0 8cea727 +papi v0.1.1 168bc9d +ic-pub-key v1.0.1 f89fa55 icp-cli v0.2.3 caeac37 motoko v1.5.1 # tag 8e4c02d not in shallow clone 395ce0b motoko-core v2.4.0 cd37dbf diff --git a/.sources/chain-fusion-signer b/.sources/chain-fusion-signer new file mode 160000 index 00000000..8cea7275 --- /dev/null +++ b/.sources/chain-fusion-signer @@ -0,0 +1 @@ +Subproject commit 8cea7275b5e80c375087a7cdffe6d1afb9bddbc8 diff --git a/.sources/ic-pub-key b/.sources/ic-pub-key new file mode 160000 index 00000000..f89fa550 --- /dev/null +++ b/.sources/ic-pub-key @@ -0,0 +1 @@ +Subproject commit f89fa550c1f5619d8af546aea235841eed702e51 diff --git a/.sources/papi b/.sources/papi new file mode 160000 index 00000000..168bc9de --- /dev/null +++ b/.sources/papi @@ -0,0 +1 @@ +Subproject commit 168bc9de51d7439af13ec0d455663d9691accd4a diff --git a/AGENTS.md b/AGENTS.md index cbf085ce..46ef62d2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -682,6 +682,9 @@ For current release hashes, see `.sources/VERSIONS`. | `.sources/cdk-rs` | `dfinity/cdk-rs` | latest release | Rust CDK (`ic-cdk`, `ic-cdk-timers`, `ic-cdk-macros`) — API signatures, management canister types | | `.sources/candid` | `dfinity/candid` | latest release | Candid spec, type system, `didc` tool source | | `.sources/response-verification` | `dfinity/response-verification` | latest release | Response verification, certified variables, certificate trees | +| `.sources/chain-fusion-signer` | `dfinity/chain-fusion-signer` | latest release | Chain Fusion Signer canister — API, key derivation, address generation, ICRC-2 payment model | +| `.sources/papi` | `dfinity/papi` | latest release | PAPI (payment API) — cycle payment interface used by the Chain Fusion Signer | +| `.sources/ic-pub-key` | `dfinity/ic-pub-key` | latest release | `@dfinity/ic-pub-key` CLI tool for deriving public keys and addresses via the Chain Fusion Signer | | `.sources/dotskills` | `vincentkoc/dotskills` | `main` | Technical documentation skill (AGPL-3.0 — kept as submodule to avoid license mixing) | ### Submodule initialization @@ -766,6 +769,9 @@ EOF | `candid` | Check for spec changes that affect the Candid reference page or type-mapping examples | | `response-verification` | Check for API changes affecting certified variables patterns in docs | | `dotskills` | Check if the `technical-documentation` skill changed in ways that affect review criteria or authoring rules | +| `chain-fusion-signer` | Check for changed canister IDs, API methods, or key derivation patterns — grep chain-fusion guide and any pages referencing the signer | +| `papi` | Check for changed payment interface or cycle cost model — update any payment setup examples in the chain-fusion signer guide | +| `ic-pub-key` | Check for changed CLI flags or commands — update CLI examples in the chain-fusion signer guide | ### Synced files from submodules From 19c14fcb1ac591efa3279e4df36cb9f49cdc3c53 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 17 Apr 2026 13:33:29 +0200 Subject: [PATCH 2/8] docs: add Chain Fusion Signer usage guide Covers ICRC-2 cycle payment setup, Ethereum and Bitcoin address derivation, transaction signing, generic ECDSA/Schnorr, web app integration pattern, fee table, and offline address derivation via @dfinity/ic-pub-key. Source: chain-fusion-signer v0.3.0, papi v0.1.1, ic-pub-key v1.0.1. Closes #24. --- .../chain-fusion/chain-fusion-signer.md | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 docs/guides/chain-fusion/chain-fusion-signer.md diff --git a/docs/guides/chain-fusion/chain-fusion-signer.md b/docs/guides/chain-fusion/chain-fusion-signer.md new file mode 100644 index 00000000..c677d175 --- /dev/null +++ b/docs/guides/chain-fusion/chain-fusion-signer.md @@ -0,0 +1,267 @@ +--- +title: "Chain Fusion Signer" +description: "Use the Chain Fusion Signer canister to sign transactions for Bitcoin, Ethereum, and other chains from web apps and the command line — no backend canister required." +--- + +The Chain Fusion Signer is a public canister on ICP that exposes the IC's threshold signature APIs directly to web apps and CLI users. Normally, accessing threshold ECDSA or Schnorr requires deploying your own backend canister. With the Chain Fusion Signer, you call a shared, governance-controlled canister instead. + +**Canister ID (mainnet):** `grghe-syaaa-aaaar-qabyq-cai` + +The signer charges callers in cycles for each API call. You pre-approve the signer to withdraw from your cycles ledger account using ICRC-2 before making calls. + +## Prerequisites + +- An ICP identity with cycles in the [Cycles Ledger](https://learn.internetcomputer.org/hc/en-us/articles/34574082975252-Cycles-Ledger) (`um5iw-rqaaa-aaaaq-qaaba-cai`) +- icp-cli installed and authenticated (`icp identity whoami`) +- For offline address derivation: Node.js and `npx` + +## Approve payment + +Every signer API call deducts cycles from your cycles ledger account. Before calling the signer, approve it to spend cycles on your behalf. One approval covers multiple calls until the allowance is exhausted. + +```bash +SIGNER="grghe-syaaa-aaaar-qabyq-cai" +CYCLES_LEDGER="um5iw-rqaaa-aaaaq-qaaba-cai" + +# Approve 1 trillion cycles — enough for ~27 signing operations +icp canister call "$CYCLES_LEDGER" icrc2_approve \ + "(record { + amount = 1_000_000_000_000 : nat; + spender = record { owner = principal \"${SIGNER}\" }; + })" \ + --network ic +``` + +See [API fees](#api-fees) for per-method costs. + +## Get your Ethereum address + +Each principal has a deterministic Ethereum address on the signer. Retrieve it with: + +```bash +icp canister call "$SIGNER" eth_address_of_caller \ + '(opt variant { CallerPaysIcrc2Cycles })' \ + --network ic +``` + +```candid +(variant { Ok = record { address = "0xf53e047376e37eAc56d48245B725c47410cf6F1e" } }) +``` + +To look up the address of another principal: + +```bash +icp canister call "$SIGNER" eth_address \ + "(record { \"principal\" = opt principal \"\" }, + opt variant { CallerPaysIcrc2Cycles })" \ + --network ic +``` + +### Derive offline (no cycles) + +Address derivation involves no secret key material, so it can be done offline using `@dfinity/ic-pub-key`: + +```bash +npx @dfinity/ic-pub-key signer eth address -u +``` + +This produces the same address as the onchain call but costs no cycles. + +## Sign an Ethereum transaction + +Build and sign an EIP-1559 transaction: + +```bash +icp canister call "$SIGNER" eth_sign_transaction \ + "(record { + to = \"0xRecipientAddress\"; + chain_id = 1 : nat; + nonce = 0 : nat; + gas = 21000 : nat; + max_fee_per_gas = 20_000_000_000 : nat; + max_priority_fee_per_gas = 1_000_000_000 : nat; + value = 1_000_000_000_000_000_000 : nat; + data = null; + }, + opt variant { CallerPaysIcrc2Cycles })" \ + --network ic +``` + +To sign a pre-hashed message: + +```bash +icp canister call "$SIGNER" eth_sign_prehash \ + "(record { hash = \"0x<32-byte-hex-hash>\" }, + opt variant { CallerPaysIcrc2Cycles })" \ + --network ic +``` + +## Get your Bitcoin address + +```bash +icp canister call "$SIGNER" btc_caller_address \ + "(record { network = variant { mainnet }; address_type = variant { P2WPKH } }, + opt variant { CallerPaysIcrc2Cycles })" \ + --network ic +``` + +### Derive offline (no cycles) + +```bash +npx @dfinity/ic-pub-key signer btc address -u -n mainnet +``` + +For testnet, use `-n testnet`. + +## Check your Bitcoin balance + +```bash +icp canister call "$SIGNER" btc_caller_balance \ + "(record { + network = variant { mainnet }; + address_type = variant { P2WPKH }; + min_confirmations = null; + }, + opt variant { CallerPaysIcrc2Cycles })" \ + --network ic +``` + +## Send Bitcoin + +```bash +icp canister call "$SIGNER" btc_caller_send \ + "(record { + network = variant { mainnet }; + address_type = variant { P2WPKH }; + utxos_to_spend = vec {}; + fee_satoshis = null; + outputs = vec { + record { + destination_address = \"bc1qRecipientAddress\"; + sent_satoshis = 10000 : nat64; + } + }; + }, + opt variant { CallerPaysIcrc2Cycles })" \ + --network ic +``` + +`utxos_to_spend` selects specific UTXOs. Pass `vec {}` to let the signer choose automatically. + +## Generic ECDSA signing + +Use `generic_sign_with_ecdsa` when you need raw threshold ECDSA signatures for chains the signer does not have dedicated methods for: + +```bash +icp canister call "$SIGNER" generic_sign_with_ecdsa \ + "(opt variant { CallerPaysIcrc2Cycles }, + record { + key_id = record { name = \"key_1\"; curve = variant { secp256k1 } }; + derivation_path = vec { blob \"my_app\"; blob \"user_key_1\" }; + message_hash = blob \"<32-byte-message-hash>\"; + })" \ + --network ic +``` + +Use a stable derivation path to get the same key every time. Different paths yield independent keys. + +## Schnorr signing + +```bash +icp canister call "$SIGNER" schnorr_sign \ + "(record { + key_id = record { algorithm = variant { ed25519 }; name = \"key_1\" }; + derivation_path = vec { blob \"my_app\"; blob \"user_key_1\" }; + message = blob \"\"; + }, + opt variant { CallerPaysIcrc2Cycles })" \ + --network ic +``` + +## Web app integration + +In a web app, call the signer from the browser using a generated TypeScript actor. Generate bindings from the signer's Candid interface: + +```bash +# Download the signer's Candid interface +icp canister metadata "$SIGNER" candid:service --network ic > signer.did + +# Generate TypeScript bindings +icp generate signer.did --output src/declarations/signer +``` + +Then create actors for both the Cycles Ledger (for payment approval) and the signer: + +```typescript +import { createActor as createCyclesLedgerActor } from './declarations/cycles_ledger'; +import { createActor as createSignerActor } from './declarations/signer'; + +async function approveAndSign(identity: Identity, messageHash: string) { + const agent = await HttpAgent.create({ identity }); + + const cyclesLedger = createCyclesLedgerActor('um5iw-rqaaa-aaaaq-qaaba-cai', { agent }); + const signer = createSignerActor('grghe-syaaa-aaaar-qabyq-cai', { agent }); + + // Pre-approve 1 trillion cycles + await cyclesLedger.icrc2_approve({ + amount: 1_000_000_000_000n, + spender: { owner: Principal.fromText('grghe-syaaa-aaaar-qabyq-cai'), subaccount: [] }, + expires_at: [], + fee: [], + memo: [], + from_subaccount: [], + created_at_time: [], + expected_allowance: [], + }); + + // Sign the prehash + const result = await signer.eth_sign_prehash( + { hash: messageHash }, + [{ CallerPaysIcrc2Cycles: null }] + ); + + if ('Err' in result) throw new Error(JSON.stringify(result.Err)); + return result.Ok.signature; +} +``` + + + +OISY Wallet uses the Chain Fusion Signer as its production signing backend and serves as a reference implementation. + +## API fees + +Fees are charged per call in cycles (at v0.3.0): + +| Method | Fee (cycles) | +|--------|-------------| +| `eth_address`, `eth_address_of_caller` | 77,000,000 | +| `btc_caller_address` | 79,000,000 | +| `generic_caller_ecdsa_public_key`, `schnorr_public_key` | 77,000,000 | +| `btc_caller_balance` | 113,000,000 | +| `eth_personal_sign`, `eth_sign_prehash`, `eth_sign_transaction` | 37,000,000,000 | +| `generic_sign_with_ecdsa`, `schnorr_sign` | 37,000,000,000 | +| `btc_caller_send`, `btc_caller_sign` | 132,000,000,000 | + +Fees are set at approximately 140% of the typical call cost to cover failed-call overhead. + +## Payment variants + +The `opt PaymentType` argument accepts these variants: + +| Variant | Description | +|---------|-------------| +| `CallerPaysIcrc2Cycles` | Caller pre-approves the Cycles Ledger; recommended for CLI and web apps | +| `CallerPaysIcrc2Tokens { ledger }` | Pay with any ICRC-2 token (ckBTC, ckETH, etc.) | +| `PatronPaysIcrc2Cycles { owner; subaccount }` | A patron account covers costs on the caller's behalf | +| `AttachedCycles` | Cycles attached directly to the call (requires proxy canister support) | + +Pass `null` instead of a payment type to use the canister's default, which is `CallerPaysIcrc2Cycles`. + +## Next steps + +- [Bitcoin integration guide](bitcoin.md) — build a full Bitcoin dapp with your own signing backend +- [Ethereum integration guide](ethereum.md) — EVM RPC canister for reading Ethereum state +- [Cycles Ledger](https://learn.internetcomputer.org/hc/en-us/articles/34574082975252-Cycles-Ledger) — fund your account with cycles + + From 8c3834941477cfab7cbac6ad8a19f9f20c2f56ed Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 17 Apr 2026 13:42:13 +0200 Subject: [PATCH 3/8] fix: add sidebar order and use internal Cycles Ledger link --- docs/guides/chain-fusion/chain-fusion-signer.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/guides/chain-fusion/chain-fusion-signer.md b/docs/guides/chain-fusion/chain-fusion-signer.md index c677d175..98df5324 100644 --- a/docs/guides/chain-fusion/chain-fusion-signer.md +++ b/docs/guides/chain-fusion/chain-fusion-signer.md @@ -1,6 +1,8 @@ --- title: "Chain Fusion Signer" description: "Use the Chain Fusion Signer canister to sign transactions for Bitcoin, Ethereum, and other chains from web apps and the command line — no backend canister required." +sidebar: + order: 5 --- The Chain Fusion Signer is a public canister on ICP that exposes the IC's threshold signature APIs directly to web apps and CLI users. Normally, accessing threshold ECDSA or Schnorr requires deploying your own backend canister. With the Chain Fusion Signer, you call a shared, governance-controlled canister instead. @@ -11,7 +13,7 @@ The signer charges callers in cycles for each API call. You pre-approve the sign ## Prerequisites -- An ICP identity with cycles in the [Cycles Ledger](https://learn.internetcomputer.org/hc/en-us/articles/34574082975252-Cycles-Ledger) (`um5iw-rqaaa-aaaaq-qaaba-cai`) +- An ICP identity with cycles in the [Cycles Ledger](../../reference/system-canisters.md#cycles-ledger) (`um5iw-rqaaa-aaaaq-qaaba-cai`) - icp-cli installed and authenticated (`icp identity whoami`) - For offline address derivation: Node.js and `npx` @@ -262,6 +264,6 @@ Pass `null` instead of a payment type to use the canister's default, which is `C - [Bitcoin integration guide](bitcoin.md) — build a full Bitcoin dapp with your own signing backend - [Ethereum integration guide](ethereum.md) — EVM RPC canister for reading Ethereum state -- [Cycles Ledger](https://learn.internetcomputer.org/hc/en-us/articles/34574082975252-Cycles-Ledger) — fund your account with cycles +- [Cycles Ledger](../../reference/system-canisters.md#cycles-ledger) — fund your account with cycles From 1be6c51eb06c7f34c492ad78b7814d1ef0c3debf Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 17 Apr 2026 13:51:48 +0200 Subject: [PATCH 4/8] fix: use @icp-sdk/bindgen instead of non-existent icp generate command --- docs/guides/chain-fusion/chain-fusion-signer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/chain-fusion/chain-fusion-signer.md b/docs/guides/chain-fusion/chain-fusion-signer.md index 98df5324..90baf96e 100644 --- a/docs/guides/chain-fusion/chain-fusion-signer.md +++ b/docs/guides/chain-fusion/chain-fusion-signer.md @@ -188,8 +188,8 @@ In a web app, call the signer from the browser using a generated TypeScript acto # Download the signer's Candid interface icp canister metadata "$SIGNER" candid:service --network ic > signer.did -# Generate TypeScript bindings -icp generate signer.did --output src/declarations/signer +# Generate TypeScript bindings (requires @icp-sdk/bindgen) +npx @icp-sdk/bindgen --did-file signer.did --out-dir src/declarations/signer ``` Then create actors for both the Cycles Ledger (for payment approval) and the signer: From 18cc456c9ab3f01f0f9d62dd86c8875af471a21b Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 17 Apr 2026 14:02:34 +0200 Subject: [PATCH 5/8] fix: link to signer repo and add source link to fee table --- docs/guides/chain-fusion/chain-fusion-signer.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/chain-fusion/chain-fusion-signer.md b/docs/guides/chain-fusion/chain-fusion-signer.md index 90baf96e..49d034a8 100644 --- a/docs/guides/chain-fusion/chain-fusion-signer.md +++ b/docs/guides/chain-fusion/chain-fusion-signer.md @@ -5,7 +5,7 @@ sidebar: order: 5 --- -The Chain Fusion Signer is a public canister on ICP that exposes the IC's threshold signature APIs directly to web apps and CLI users. Normally, accessing threshold ECDSA or Schnorr requires deploying your own backend canister. With the Chain Fusion Signer, you call a shared, governance-controlled canister instead. +The [Chain Fusion Signer](https://github.com/dfinity/chain-fusion-signer) is a public canister on ICP that exposes the IC's threshold signature APIs directly to web apps and CLI users. Normally, accessing threshold ECDSA or Schnorr requires deploying your own backend canister. With the Chain Fusion Signer, you call a shared, governance-controlled canister instead. **Canister ID (mainnet):** `grghe-syaaa-aaaar-qabyq-cai` @@ -233,7 +233,7 @@ OISY Wallet uses the Chain Fusion Signer as its production signing backend and s ## API fees -Fees are charged per call in cycles (at v0.3.0): +Fees are charged per call in cycles. Verify against the [source](https://github.com/dfinity/chain-fusion-signer/blob/main/src/signer/api/src/methods.rs) for the latest values (table reflects v0.3.0): | Method | Fee (cycles) | |--------|-------------| From e70805991e1b1bc042c374b8ecaf722cf381bafe Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 17 Apr 2026 14:07:22 +0200 Subject: [PATCH 6/8] docs: mention papi as the payment protocol behind PaymentType variants --- docs/guides/chain-fusion/chain-fusion-signer.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/guides/chain-fusion/chain-fusion-signer.md b/docs/guides/chain-fusion/chain-fusion-signer.md index 49d034a8..26f55d21 100644 --- a/docs/guides/chain-fusion/chain-fusion-signer.md +++ b/docs/guides/chain-fusion/chain-fusion-signer.md @@ -260,10 +260,12 @@ The `opt PaymentType` argument accepts these variants: Pass `null` instead of a payment type to use the canister's default, which is `CallerPaysIcrc2Cycles`. +These variants are defined by [papi](https://github.com/dfinity/papi), an open-source Rust library for adding payment gateways to ICP canisters. The Chain Fusion Signer uses papi internally to handle fee collection. If you want to charge callers in your own canister — using the same `CallerPaysIcrc2Cycles` or `PatronPaysIcrc2Cycles` patterns — papi provides the implementation. + ## Next steps - [Bitcoin integration guide](bitcoin.md) — build a full Bitcoin dapp with your own signing backend - [Ethereum integration guide](ethereum.md) — EVM RPC canister for reading Ethereum state - [Cycles Ledger](../../reference/system-canisters.md#cycles-ledger) — fund your account with cycles - + From 834e8d6cdc4277eed0cd45991427f037f0c97e36 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 17 Apr 2026 14:25:08 +0200 Subject: [PATCH 7/8] fix: correct token payment variants table and add OISY patron pattern note --- docs/guides/chain-fusion/chain-fusion-signer.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/guides/chain-fusion/chain-fusion-signer.md b/docs/guides/chain-fusion/chain-fusion-signer.md index 26f55d21..6ccb4e3f 100644 --- a/docs/guides/chain-fusion/chain-fusion-signer.md +++ b/docs/guides/chain-fusion/chain-fusion-signer.md @@ -229,7 +229,7 @@ async function approveAndSign(identity: Identity, messageHash: string) { -OISY Wallet uses the Chain Fusion Signer as its production signing backend and serves as a reference implementation. +OISY Wallet uses the Chain Fusion Signer as its production signing backend and serves as a reference implementation. OISY uses `PatronPaysIcrc2Cycles` — the OISY backend canister pre-approves cycles on each user's behalf, so individual users pay no cycles directly. ## API fees @@ -254,12 +254,15 @@ The `opt PaymentType` argument accepts these variants: | Variant | Description | |---------|-------------| | `CallerPaysIcrc2Cycles` | Caller pre-approves the Cycles Ledger; recommended for CLI and web apps | -| `CallerPaysIcrc2Tokens { ledger }` | Pay with any ICRC-2 token (ckBTC, ckETH, etc.) | +| `CallerPaysIcrc2Tokens { ledger }` | Pay via ICRC-2 token transfer; for this canister the ledger is hardcoded to the Cycles Ledger | | `PatronPaysIcrc2Cycles { owner; subaccount }` | A patron account covers costs on the caller's behalf | +| `PatronPaysIcrc2Tokens { owner; subaccount }` | Patron pays via ICRC-2 token; for this canister the ledger is hardcoded to the Cycles Ledger | | `AttachedCycles` | Cycles attached directly to the call (requires proxy canister support) | Pass `null` instead of a payment type to use the canister's default, which is `CallerPaysIcrc2Cycles`. +**Note on token variants:** `CallerPaysIcrc2Tokens` and `PatronPaysIcrc2Tokens` are supported by this canister, but the ledger is hardcoded to the Cycles Ledger — they do not accept arbitrary tokens such as ckBTC or ckETH. All five variants settle in cycles. + These variants are defined by [papi](https://github.com/dfinity/papi), an open-source Rust library for adding payment gateways to ICP canisters. The Chain Fusion Signer uses papi internally to handle fee collection. If you want to charge callers in your own canister — using the same `CallerPaysIcrc2Cycles` or `PatronPaysIcrc2Cycles` patterns — papi provides the implementation. ## Next steps From a3b98a98818bac510deb29173f14b2f99496d95d Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 17 Apr 2026 15:13:29 +0200 Subject: [PATCH 8/8] docs: add papi forward link to next steps --- docs/guides/chain-fusion/chain-fusion-signer.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/guides/chain-fusion/chain-fusion-signer.md b/docs/guides/chain-fusion/chain-fusion-signer.md index 6ccb4e3f..2194926c 100644 --- a/docs/guides/chain-fusion/chain-fusion-signer.md +++ b/docs/guides/chain-fusion/chain-fusion-signer.md @@ -270,5 +270,6 @@ These variants are defined by [papi](https://github.com/dfinity/papi), an open-s - [Bitcoin integration guide](bitcoin.md) — build a full Bitcoin dapp with your own signing backend - [Ethereum integration guide](ethereum.md) — EVM RPC canister for reading Ethereum state - [Cycles Ledger](../../reference/system-canisters.md#cycles-ledger) — fund your account with cycles +- [papi](https://github.com/dfinity/papi) — add the same `CallerPaysIcrc2Cycles` / `PatronPaysIcrc2Cycles` payment pattern to your own canister