Skip to content

docs: chain-key tokens guide#76

Merged
marc0olo merged 2 commits into
mainfrom
docs/guides-defi-chain-key-tokens
Apr 16, 2026
Merged

docs: chain-key tokens guide#76
marc0olo merged 2 commits into
mainfrom
docs/guides-defi-chain-key-tokens

Conversation

@marc0olo
Copy link
Copy Markdown
Member

Summary

  • Explains how ckBTC and ckETH maintain their peg via threshold cryptography (no bridges, no custodians)
  • Covers full deposit and withdrawal flows for ckBTC (get_btc_addressupdate_balance to mint; icrc2_approveretrieve_btc_with_approval to redeem) with Motoko and Rust examples
  • ckETH deposit/withdrawal via CLI examples; ICRC-1 transfer usage for both tokens
  • Subaccount derivation pattern for per-user deposit addresses
  • Common pitfalls: wrong minter ID, missing update_balance, minimum withdrawal amounts, fee handling, subaccount byte size
  • Uses .mdx for Motoko/Rust language tab components

Sync recommendation

informed by dfinity/icskills — ckbtc skill, icrc-ledger skill; dfinity/portal — chain-key-tokens pages

Covers ckBTC and ckETH minting (deposit), redemption (withdrawal), ledger transfers, and subaccount derivation. Includes Motoko and Rust implementations with language tabs. Renamed stub from .md to .mdx for language tabs.
@marc0olo
Copy link
Copy Markdown
Member Author

Review: Chain-Key Tokens

Must fix

  • ckETH deposit flow is factually incorrect: Lines 432–434 describe the deposit as: "Call minter_address on the ckETH minter to get the Ethereum address of the ckETH helper contract. This address is unique per ICP principal." Both claims are wrong. Per the portal source (defi/chain-key-tokens/cketh/overview.mdx): (1) There is no minter_address function — the user calls the deposit function on the shared helper smart contract on Ethereum, passing their ICP principal as call data. The helper contract address is a single shared contract (e.g., 0x6abDA0438307733FC299e9C229FD3cc074bD8cC0 on mainnet), not unique per principal. (2) The per-user identifier is the ICP principal encoded in the deposit call data, not a unique contract address. The flow should read: the user calls the deposit function of the shared ckETH helper contract on Ethereum, with some ETH and their ICP principal as call data; the minter monitors ReceivedEth events and mints ckETH to the corresponding ICRC-1 account. Fix the deposit steps and remove the false minter_address function claim.

  • Internal link preferred over external URL for chain-key cryptography: Line 20 links to https://learn.internetcomputer.org/hc/en-us/articles/34211397080980 for the chain-key cryptography explanation. An internal page exists at docs/concepts/chain-key-cryptography.md. Per project rules, internal pages must be preferred over external URLs. Change the link to ../../concepts/chain-key-cryptography.md.

  • File is .mdx but Tabs usage should be reviewed: The page uses <Tabs syncKey="lang"> with Motoko/Rust tabs, which justifies .mdx. This is consistent with project policy. No action required on the file extension itself.

Suggestions

  • created_at_time = null in withdrawal code loses dedup protection: The Motoko withdrawToBtc function (line 305) and the Rust withdraw_to_btc function (line 389) both set created_at_time = null in the icrc2_approve call. Per the icrc-ledger skill (pitfall docs: getting-started quickstart page #5), omitting created_at_time means two identical transfers submitted within 24h will both execute. For production-quality example code, set created_at_time to Time.now() (Motoko) or ic_cdk::api::time() (Rust). This applies to both the approve call and the transfer. A comment warning the reader about this trade-off would be acceptable if the author prefers brevity.

  • ckBTC checker canister ID missing from canister table: The page mentions "The ckBTC checker canister publishes a public audit of reserves" (line 22) but the checker canister ID (oltsj-fqaaa-aaaar-qal5q-cai) is not in the canister IDs table. The portal source (defi/chain-key-tokens/ckbtc/overview.mdx) and the ckbtc skill both list it. Developers who want to verify reserves need this ID. Either add it to the table or remove the reference to the checker.

  • Upstream comment uses Learn Hub URL instead of internal reference: The <!-- Upstream: --> comment at the bottom references learn.internetcomputer.org/hc/en-us/articles/34211397080980. This should either not be listed here (the Upstream comment should reference .sources/ repos, not external URLs) or replaced with the relevant internal sources. The page body also lists the ckbtc and icrc-ledger icskills but does not list the portal source material (dfinity/portal defi/chain-key-tokens/), which was used as source material for the ckETH flow. Update the Upstream comment to accurately reflect consulted sources.

  • Reader test — opening delivers on the title: The opening two paragraphs clearly explain what chain-key tokens are and what the guide covers. Good. However, the flow description note at line 60–61 ("The minter requires a minimum number of Bitcoin confirmations before minting (currently 6 on mainnet)") uses "currently" which implies it may change. This is fine as a caveat, but the phrase should link to or note where the confirmation count can be verified.

  • Content brief coverage — JS SDK not addressed: The stub <!-- Source Material --> lists @icp-sdk/canisters (https://js.icp.build/canisters) as a source. The page has no JavaScript examples. The brief doesn't explicitly require JS code, but the absence is worth noting if the audience includes frontend developers who want to call ckBTC/ckETH from a web app. Consider a brief mention or a cross-reference to the JS SDK docs.

Verified

  • All internal links resolve: token-ledgers.md resolves to token-ledgers.mdx, ../chain-fusion/bitcoin.md resolves to bitcoin.mdx, ../chain-fusion/ethereum.md resolves to ethereum.mdx, wallet-integration.md and ../../reference/token-standards.md both resolve. All links valid per Astro .md.mdx resolution rules.
  • No dfx references found.
  • No mo:base imports found; all Motoko imports use mo:core.
  • No internetcomputer.org/docs/ or docs.internetcomputer.org banned URLs found.
  • CLI commands verified against .sources/icp-cli/docs/reference/cli.md: icp canister call exists, -e flag for environment override is valid, -e ic for mainnet is correct usage.
  • Frontmatter is complete: title, description, and sidebar.order are all present and consistent with the page content.
  • <!-- Upstream: --> comment is present (line 621) — satisfies the CI-enforced requirement.
  • Canister IDs verified against the ckbtc icskill and portal source: ckBTC Ledger mxzaz-hqaaa-aaaar-qaada-cai, ckBTC Minter mqygn-kiaaa-aaaar-qaadq-cai, ckBTC Index n5wcd-faaaa-aaaar-qaaea-cai, ckETH Ledger ss2fx-dyaaa-aaaar-qacoq-cai, ckETH Minter sv3dd-oaaaa-aaaar-qacoa-cai, ckETH Index s3zol-vqaaa-aaaar-qacpa-cai all match sources. Testnet4 IDs also match.
  • Nat32/Nat64 used only as types in the deposit Motoko code (no module function calls) — these are Motoko built-in primitive types that do not require imports.
  • withdraw_eth function name for ckETH withdrawal verified in portal source.
  • Subaccount derivation (length-prefix + zero-padding to 32 bytes) matches the ckbtc skill implementation.
  • ckBTC .mdx tabs use correct order: Motoko first, Rust second.
  • Minimum withdrawal amount of 50,000 satoshis verified against the ckbtc skill (pitfall docs: getting-started project structure page #6).
  • Fee amounts (10 satoshis for ckBTC) verified against the ckbtc skill and icrc-ledger skill.

- Fix factually incorrect ckETH deposit flow: remove false minter_address
  function claim and unique-per-principal address claim; describe the correct
  flow using the deposit function on the shared helper contract with ICP
  principal as call data, and the minter monitoring ReceivedEth events
- Change chain-key cryptography link from external Learn Hub URL to internal
  page (docs/concepts/chain-key-cryptography.md) per CLAUDE.md linking rules
- Add ckBTC Checker canister ID (oltsj-fqaaa-aaaar-qal5q-cai) to the canister
  table since the page already references the checker's public audit
- Add created_at_time for dedup protection in withdrawal code (Motoko and Rust)
  with explanatory comment per icrc-ledger skill pitfall #5
- Update Upstream comment to reference .sources/ repos instead of external URL
@marc0olo
Copy link
Copy Markdown
Member Author

Feedback addressed for PR #76 (chain-key tokens guide):

Changes applied

Must fix — ckETH deposit flow corrected (lines 431–434)
Removed the false minter_address function claim and the false "unique per ICP principal" address claim. The correct flow per the portal source (dfinity/portal docs/defi/chain-key-tokens/cketh/overview.mdx):

  • The user calls the deposit function on the shared ckETH helper smart contract on Ethereum (mainnet: 0x6abDA0438307733FC299e9C229FD3cc074bD8cC0), passing some ETH and their ICP principal as call data.
  • The helper contract emits a ReceivedEth event. The ckETH minter monitors this event by periodically fetching Ethereum logs from RPC providers and mints ckETH to the corresponding ICRC-1 account.

Must fix — Internal link for chain-key cryptography (line 20)
Changed https://learn.internetcomputer.org/hc/en-us/articles/34211397080980 to ../../concepts/chain-key-cryptography.md (the internal page exists at docs/concepts/chain-key-cryptography.md). Per CLAUDE.md: internal links are required when an internal page exists.

Suggestion — ckBTC Checker canister ID added to table
Added ckBTC Checker | oltsj-fqaaa-aaaar-qal5q-cai to the ckBTC mainnet canister table. The page already referenced "The ckBTC checker canister publishes a public audit of reserves" — developers who want to verify reserves need this ID. Verified against the ckbtc icskill.

Suggestion — created_at_time set in withdrawal code (Motoko and Rust)
Added created_at_time to the icrc2_approve call in both the Motoko and Rust withdrawal examples, using Nat64.fromNat(Int.abs(Time.now())) (Motoko) and ic_cdk::api::time() (Rust) per the icrc-ledger skill pitfall #5. Added explanatory comments explaining the deduplication trade-off. Added Int and Time imports to the Motoko withdrawal example.

Suggestion — Upstream comment updated
Changed <!-- Upstream: ... learn.internetcomputer.org/... --> to reference the actual .sources/ material consulted: dfinity/portal docs/defi/chain-key-tokens/cketh/overview.mdx. The Upstream: comment should list .sources/ repos, not external URLs.

Items skipped

Suggestion — "currently" in confirmation count wording
The reviewer noted the phrase "currently 6 on mainnet" implies the value may change. This phrasing is appropriate — the confirmation threshold is a minter parameter that can change via governance. The word "currently" is a correct and useful caveat. No change made.

Suggestion — JS SDK examples not present
The reviewer noted the stub listed @icp-sdk/canisters as a source but the page has no JavaScript examples. This is an optional enhancement; the page's target audience is canister developers (Motoko/Rust), and the brief does not require JS examples. JS SDK cross-reference can be added in a future iteration. No change made.

Note — .mdx file extension
Reviewer confirmed no action needed: the page uses <Tabs syncKey="lang"> which correctly justifies .mdx per project policy.

Build note
The npm run build fails with a pre-existing error in docs/guides/backends/https-outcalls.mdx (missing .sources/examples submodule in this worktree). This failure exists on the base commit before any of my changes and is unrelated to the chain-key-tokens page. The chain-key-tokens.mdx file has valid MDX structure.

@marc0olo marc0olo merged commit 82e5767 into main Apr 16, 2026
1 check passed
@marc0olo marc0olo deleted the docs/guides-defi-chain-key-tokens branch April 16, 2026 15:43
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