From 6713d248b6a1b42e091cee0bea917e700fb91b6a Mon Sep 17 00:00:00 2001 From: David Richards Date: Wed, 8 Jul 2026 11:37:48 +0100 Subject: [PATCH 1/2] Build the Wallet Directory: tracking repo for Canton Network wallet features Implements the Canton Foundation's Featured Wallets Program (renamed here to "Wallet Directory" to avoid implying endorsement/promotion) as a structured, low-maintenance repo: - WALLET_DIRECTORY.md: generated summary table (two sections -- primary Canton Network features, then everything else), linking each claim to its evidence. - wallets/_feature_registry.yaml: single source of truth for feature ids, names, categories, and suggested tests. - wallets/.yaml: one self-attestation file per wallet provider, following wallets/TEMPLATE.yaml. - proofs//: one folder per wallet holding self-attestation and third-party-attestation evidence files, per proofs/_TEMPLATE/*. Verification model: self-attested (checkmark, linked to evidence), independently verified (shield + verifier name), disputed as unsupported (cross + verifier name, when a verifier shows a claim doesn't hold up), or not supported (optionally with a stated reason). A handful of generic, non-Canton-specific capabilities (key recovery, threshold signing, compliance, etc.) are marked self_attested_only and have no proof/verification mechanism at all. scripts/generate_table.py has zero dependencies -- it includes its own minimal YAML reader scoped to this repo's schema, so regenerating the page never requires pip/venv. A GitHub Action (.github/workflows/verify-wallet-directory.yml) regenerates and diffs the page on every PR touching wallets/ or scripts/. CONTRIBUTING.md is the single file explaining the whole process for both wallet providers and third-party verifiers. Also: split the combined "CIP-0103 dApp API / Wallet Connect" feature into two independently provable features; removed batch_airdrops (internal-only consideration); renamed featured_since to added. --- .github/PULL_REQUEST_TEMPLATE.md | 38 ++ .github/workflows/verify-wallet-directory.yml | 32 ++ .gitignore | 3 + CONTRIBUTING.md | 173 ++++++++ README.md | 19 +- WALLET_DIRECTORY.md | 71 +++ examples/acme-wallet.yaml | 76 ++++ proofs/_TEMPLATE/TEMPLATE-self-attestation.md | 314 ++++++++++++++ .../TEMPLATE-third-party-attestation.md | 408 ++++++++++++++++++ .../dummy-wallet-self-attestation.md | 85 ++++ .../dummy-wallet-third-party-attestation.md | 28 ++ scripts/generate_table.py | 388 +++++++++++++++++ wallets/TEMPLATE.yaml | 308 +++++++++++++ wallets/_feature_registry.yaml | 261 +++++++++++ wallets/dummy-wallet.yaml | 82 ++++ 15 files changed, 2284 insertions(+), 2 deletions(-) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/verify-wallet-directory.yml create mode 100644 .gitignore create mode 100644 CONTRIBUTING.md create mode 100644 WALLET_DIRECTORY.md create mode 100644 examples/acme-wallet.yaml create mode 100644 proofs/_TEMPLATE/TEMPLATE-self-attestation.md create mode 100644 proofs/_TEMPLATE/TEMPLATE-third-party-attestation.md create mode 100644 proofs/dummy-wallet/dummy-wallet-self-attestation.md create mode 100644 proofs/dummy-wallet/dummy-wallet-third-party-attestation.md create mode 100644 scripts/generate_table.py create mode 100644 wallets/TEMPLATE.yaml create mode 100644 wallets/_feature_registry.yaml create mode 100644 wallets/dummy-wallet.yaml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..3d3234c --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,38 @@ + + +### Type of submission + +- [ ] New wallet application +- [ ] Update to an existing wallet's feature support +- [ ] Third-party verification (confirmed, or disputed as unsupported) of an existing claim +- [ ] Marking a feature as not supported, with a reason + +### Summary + + + +### Checklist + +- [ ] `wallets/.yaml` added or updated (self-attestation) +- [ ] A heading added to `proofs//-self-attestation.md` + for every feature newly claimed as `supported: true` +- [ ] `python scripts/generate_table.py` was run and the regenerated + `WALLET_DIRECTORY.md` is included in this PR (a CI check will catch + this if it's missed, but running it locally saves a round trip) +- [ ] If applying for the first time: confirmed membership in + `#ext-temp-party-scaling` and set `slack_party_scaling_channel_joined: true` +- [ ] If marking a feature as not supported: reason filled in under that + feature's `reason:` field, not just left blank +- [ ] If this is a verification: the heading added to + `proofs//-third-party-attestation.md` + reflects evidence gathered independently, not copied from the + wallet's self-attestation, and `result` on the matching + `verified_by` entry is set to `verified` or `unsupported` to match + +### Additional context + + diff --git a/.github/workflows/verify-wallet-directory.yml b/.github/workflows/verify-wallet-directory.yml new file mode 100644 index 0000000..98835f2 --- /dev/null +++ b/.github/workflows/verify-wallet-directory.yml @@ -0,0 +1,32 @@ +name: Verify WALLET_DIRECTORY.md + +# Regenerates WALLET_DIRECTORY.md from wallets/*.yaml and fails the PR if the +# committed file doesn't match. generate_table.py has no dependencies, so +# this workflow needs no install step -- it just runs the script. + +on: + pull_request: + paths: + - "wallets/**" + - "scripts/**" + - "WALLET_DIRECTORY.md" + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Regenerate WALLET_DIRECTORY.md + run: python3 scripts/generate_table.py + + - name: Fail if WALLET_DIRECTORY.md is out of date + run: | + if ! git diff --exit-code -- WALLET_DIRECTORY.md; then + echo "::error file=WALLET_DIRECTORY.md::WALLET_DIRECTORY.md is out of date with wallets/*.yaml. Run 'python3 scripts/generate_table.py' locally and commit the regenerated file." + exit 1 + fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b908d4c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__/ +*.pyc +.DS_Store diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..30f644e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,173 @@ +# Contributing to the Wallet Directory Program + +This repo is the source of truth for the Canton Foundation's Wallet +Directory Program: which wallet providers support which Canton Network +features, and the public evidence backing each claim. This file explains +how it all works -- for both wallet providers and third-party +verifiers. + +## How the program works + +1. A wallet provider self-attests to feature support by opening a PR that + adds or edits `wallets/.yaml`, with evidence in + `proofs//-self-attestation.md`. +2. Wallet providers must also join and stay active in + `#ext-temp-party-scaling`, and be able to demonstrate sign-up + rate-limiting / circuit-breaker controls (tracked in + `slack_party_scaling_channel_joined`). +3. The Canton Foundation reviews and merges the PR. +4. Anyone -- individuals or organizations not affiliated with the wallet -- + can independently verify a claim by adding a heading with their evidence + to `proofs//-third-party-attestation.md` and a + `verified_by` entry in the wallet's YAML. +5. `WALLET_DIRECTORY.md` is generated from all `wallets/*.yaml` files and is + the public page people read. Never edit it by hand. + +## The three places data lives + +- **`WALLET_DIRECTORY.md`** -- the generated summary table everyone reads. + Never edit it by hand; it's produced by `scripts/generate_table.py`. +- **`wallets/.yaml`** -- one file per wallet provider, holding + their name, website, and their claim (`supported: true/false`) on every + feature. This is the only file a wallet provider edits to update their + listing. +- **`proofs//`** -- one folder per wallet provider, holding the + evidence behind those claims: + ``` + proofs/ + / + -self-attestation.md <- the wallet's own evidence, one heading per feature + -self-attestation-images/ <- screenshots/recordings referenced from the self-attestation.md file + -third-party-attestation.md <- independent verifiers' evidence, one heading per (feature, verifier) + -third-party-attestation-images/ <- screenshots/recordings referenced from the third-party-attestation.md file + ``` + Self-attestation and third-party verification are kept in separate files + because a verifier's evidence must be independently gathered, not copied + from the wallet's own claims -- separate files make that easy to audit. + Each heading covers one feature, formatted exactly as it appears in + `wallets/_feature_registry.yaml`, e.g.: + ```markdown + ## CC support (transfers and holding) `cc_support` + ``` + In the third-party attestation file, each heading also names the verifier + and the outcome (see "Third-party verification" below), and is unique per + (feature, verifier) pair, so more than one verifier can check the same + feature. The heading text matters: GitHub turns it into the page anchor + that `WALLET_DIRECTORY.md` and the wallet's `proof:` / + `verified_by[].proof` fields link to -- keep headings exactly as shown + and don't reuse one for two different features. Free-text features + (Wallet Type block, `key_generation_method`, `assets_supported`, + `languages_supported`) never get a heading here as there's no link to + proof. + +## Applying as a new wallet provider + +1. Copy `wallets/TEMPLATE.yaml` to `wallets/.yaml` and + fill it in -- every feature is already listed there with a description + and suggested test, in order. See `examples/acme-wallet.yaml` for a + worked (fictional) example. +2. Copy `proofs/_TEMPLATE/TEMPLATE-self-attestation.md` to + `proofs//-self-attestation.md` and + add a heading for every feature you mark `supported: true` and want to + back with evidence -- only the ones you're claiming. Put screenshots/videos in + `proofs//-self-attestation-images/` + (create that folder alongside the `.md` file) and reference them from + your heading. Unproven claims are allowed, but evidence makes your + listing more credible to readers. See + `proofs/dummy-wallet/dummy-wallet-self-attestation.md` for a worked + example. Reference the resulting file + heading anchor in that + feature's `proof:` field in your wallet YAML. + + A handful of boolean features are marked `self_attested_only: true` in + `wallets/_feature_registry.yaml` -- generic wallet capabilities (e.g. + key recovery, threshold signing, compliance certifications) that don't + require proof. For those, just set `supported` (and `reason` if false) + in your wallet YAML and skip `proof`/`verified_by` and the evidence + heading entirely -- there's no mechanism to back or verify these claims. +3. Run `python3 scripts/generate_table.py` and include the regenerated + `WALLET_DIRECTORY.md` in your PR. No install step -- it only uses the + Python standard library, so any Python 3 works. +4. Open a PR using the pull request template. A maintainer will review + and, once approved, merge your PR. + +## Updating an existing listing + +Same process, but scoped to the fields that changed. Because each wallet +has its own YAML file, your PR only touches your own file (plus the +generated `WALLET_DIRECTORY.md`) -- it won't conflict with other wallets' +submissions. + +## Marking a feature as not supported + +If you don't support a feature -- because it doesn't apply to your +product, it's not built yet, or any other reason -- you can leave the +feature out of your YAML entirely, and it'll render as "โ€”" on +`WALLET_DIRECTORY.md`. If you'd rather give readers a reason instead of +silence, set `supported: false` and add `reason: ""` +under that feature. There's no approval or waiver involved -- it's just an +optional way to be upfront about a gap instead of leaving it blank. The +cell then reads "Not supported" and links to your wallet's YAML file where +the reason is written. + +## Third-party verification + +Anyone not affiliated with the wallet provider can verify a claim, for any +feature except one marked `self_attested_only: true` in +`wallets/_feature_registry.yaml` -- those are generic wallet capabilities +that aren't Canton Network specific, so there's nothing to independently +test; they're self-attested claims only, with no verification mechanism. +To verify a feature that does support it: + +1. Independently reproduce the feature's test where one is defined (see + `suggested_test` in `wallets/_feature_registry.yaml`) -- do not simply + copy the wallet's self-attestation. +2. Copy `proofs/_TEMPLATE/TEMPLATE-third-party-attestation.md` to + `proofs//-third-party-attestation.md` (or add + a heading to the existing file if one's already there), formatted as + `## \`\` โ€” Verified by `, and fill + in your evidence underneath (screenshots/videos go in the matching + `-images/` folder). +3. Add an entry to that feature's `verified_by` list in the wallet's YAML: + ```yaml + verified_by: + - by: Your Name or Org + date: "2026-07-01" + result: verified + proof: proofs//-third-party-attestation.md# + ``` +4. Run `python3 scripts/generate_table.py` and include the regenerated + `WALLET_DIRECTORY.md` in your PR, same as a wallet provider would. +5. Open a PR. Once merged, that feature's cell on `WALLET_DIRECTORY.md` + links your name (with a ๐Ÿ›ก๏ธ) to your evidence, alongside the wallet's + own self-attestation link. + +### Unverified features + +If you can't verify a feature leave it blank. + +If instead you're able to show a feature isn't actually supported, despite +the wallet claiming `supported: true`, mark it as unsupported: + +- Add a heading `## \`\` โ€” Unsupported by ` + to the third-party attestation file, with your evidence that the feature + doesn't work as claimed. +- Add a `verified_by` entry with `result: unsupported` (instead of + `result: verified`), pointing at that heading. + +That feature's cell on `WALLET_DIRECTORY.md` then shows a โŒ next to your +name, linked to your evidence, alongside the wallet's own claim -- so +readers see the dispute rather than a plain, unchallenged โœ…. + +## Regenerating the summary page + +`WALLET_DIRECTORY.md` is generated, never hand-edited: + +``` +python3 scripts/generate_table.py +``` + +Run this locally after any change to a file under `wallets/` and commit +the result. It has no dependencies to install. A GitHub Action +(`.github/workflows/verify-wallet-directory.yml`) also regenerates and +diffs this file on every PR touching `wallets/` or `scripts/`, and fails +the check if it's out of sync, in case you forget. diff --git a/README.md b/README.md index 71639ea..65f08db 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,17 @@ -# wallets -Wallets +# Wallets + +Source of truth for the Canton Foundation's **Wallet Directory Program**: +which wallet providers support which Canton Network features, and the +public evidence backing each claim. + +- **[WALLET_DIRECTORY.md](./WALLET_DIRECTORY.md)** -- the generated summary + page. Start here. +- **[CONTRIBUTING.md](./CONTRIBUTING.md)** -- how to add a wallet, update a + listing, or verify someone else's claim. +- **`wallets/`** -- one YAML file per wallet provider (their + self-attestation) plus `_feature_registry.yaml`, the single source of + truth for feature IDs, names, and required-feature tests. +- **`proofs/`** -- evidence backing each claim, self-attested and + independently verified, organized by wallet and feature. +- **`scripts/generate_table.py`** -- regenerates `WALLET_DIRECTORY.md` from + the `wallets/` directory. \ No newline at end of file diff --git a/WALLET_DIRECTORY.md b/WALLET_DIRECTORY.md new file mode 100644 index 0000000..fad3d6d --- /dev/null +++ b/WALLET_DIRECTORY.md @@ -0,0 +1,71 @@ + + +# Wallet Directory + +Source of truth for Canton Network wallet providers' supported features and +assets, per the Wallet Directory Program (see [CONTRIBUTING.md](./CONTRIBUTING.md)). +Last generated 2026-07-08 10:34 UTC. + +**Legend:** โœ… self-attested -- click through to the evidence ยท ๐Ÿ›ก๏ธ *name* verified by an +independent third party -- click the name for their evidence ยท โŒ *name* found this claim to +be unsupported, contradicting the wallet -- click the name for their evidence ยท a linked +"Not supported" means the wallet has said no with a reason ยท +โ€” no claim made either way. + +## Wallet Overview & Primary Canton Network Features + +| Feature | [Dummy Wallet](wallets/dummy-wallet.yaml) | +|---|---| +| **Wallet Overview** | | +|     Website | [https://example.com/dummy-wallet](https://example.com/dummy-wallet) | +|     Added | 2026-06-10 | +| **Wallet Type** | | +|     Retail / Enterprise | retail, enterprise | +|     Custody Type | self-custodial | +|     Form factor (Mobile / Browser / Desktop / Browser Extension / Hardware) | browser, mobile | +|     Deployment Model (Self-hosted / SaaS / Hybrid) | saas, hybrid | +| **Canton Coin** | | +|     CC support (transfers and holding) | [โœ…](proofs/dummy-wallet/dummy-wallet-self-attestation.md#cc-support-transfers-and-holding-cc_support) ๐Ÿ›ก๏ธ[Blockdaemon](proofs/dummy-wallet/dummy-wallet-third-party-attestation.md#cc-support-transfers-and-holding-cc_support-verified-by-blockdaemon) | +|     Pre-approvals for CC | [โœ…](proofs/dummy-wallet/dummy-wallet-self-attestation.md#pre-approvals-for-cc-preapprovals) | +| **Token Standard** | | +|     CIP-0056 token standard transfer support | [โœ…](proofs/dummy-wallet/dummy-wallet-self-attestation.md#cip-0056-token-standard-transfer-support-cip_0056_transfer) | +|     CIP-0056 token standard allocation support | โ€” | +|     CIP-112 token standard v2 support | โœ… | +| **Interoperability** | | +|     CIP-0103 dApp API support | [โœ…](proofs/dummy-wallet/dummy-wallet-self-attestation.md#cip-0103-dapp-api-support-cip_0103_dapp_api) | +|     Wallet Connect support | [Not supported](wallets/dummy-wallet.yaml) | +|     Memo tag support for transfers to exchanges | [โœ…](proofs/dummy-wallet/dummy-wallet-self-attestation.md#memo-tag-support-for-transfers-to-exchanges-memo_tag_support) | + +## Other Features + +| Feature | [Dummy Wallet](wallets/dummy-wallet.yaml) | +|---|---| +| **App Specific** | | +|     Pre-approvals for DA Registry issued assets | โ€” | +|     Wallet Gateway signing driver | โ€” | +| **Party Specific** | | +|     Key generation method | โ€” | +|     Key recovery | โœ… | +|     Social recovery | โ€” | +|     Multi-address / account | โ€” | +|     Distributed parties | โ€” | +| **Network relevant features** | | +|     UTXO merge delegation contract enabled | โ€” | +| **Transaction Signing** | | +|     Policy workflows | โ€” | +|     Threshold Signature Scheme | โ€” | +|     Hardware wallet support | โœ… | +| **Wallet Capabilities** | | +|     Compliance | โ€” | +|     Validator hosting (self-hosted / BYOV) | โ€” | +|     Tokenization | โ€” | +|     Reward minting | โ€” | +|     Transfer object / proof of transfer support | โ€” | +|     Clear signing | โœ… | +|     Integrated swaps | โ€” | +|     Assets supported | CC, USDC (CIP-0056), Example Token (CIP-0056) | +|     Languages supported | English, German | diff --git a/examples/acme-wallet.yaml b/examples/acme-wallet.yaml new file mode 100644 index 0000000..e5e98ac --- /dev/null +++ b/examples/acme-wallet.yaml @@ -0,0 +1,76 @@ +# EXAMPLE ONLY -- demonstrates the wallets/*.yaml schema and how +# scripts/generate_table.py renders it. "Acme Wallet" is not a real, +# endorsed, or claimed wallet. Do not put this file in wallets/. +# To add a real wallet, copy wallets/TEMPLATE.yaml instead. + +name: "Acme Wallet" +wallet-name: acme-wallet +website: "https://example.com/acme-wallet" +contact: "wallet-team@example.com" +added: "2026-05-15" +slack_party_scaling_channel_joined: true + +features: + # Wallet Type + wallet_model: [retail] + custody_model: self-custodial + form_factor: [mobile, browser-extension] + deployment_model: [saas] + + # Canton Coin + cc_support: + supported: true + proof: proofs/acme-wallet/acme-wallet-self-attestation.md#cc-support-transfers-and-holding-cc_support + verified_by: + - by: Blockdaemon + date: "2026-05-20" + result: verified + proof: proofs/acme-wallet/acme-wallet-third-party-attestation.md#cc-support-transfers-and-holding-cc_support-verified-by-blockdaemon + preapprovals: + supported: true + proof: proofs/acme-wallet/acme-wallet-self-attestation.md#pre-approvals-for-cc-preapprovals + verified_by: [] + + # Token Standard + cip_0056_transfer: + supported: true + proof: proofs/acme-wallet/acme-wallet-self-attestation.md#cip-0056-token-standard-transfer-support-cip_0056_transfer + verified_by: [] + cip_112_v2: + supported: true + proof: null + verified_by: [] + + # Interoperability + cip_0103_dapp_api: + supported: true + proof: proofs/acme-wallet/acme-wallet-self-attestation.md#cip-0103-dapp-api-support-cip_0103_dapp_api + verified_by: [] + walletconnect_support: + supported: true + proof: proofs/acme-wallet/acme-wallet-self-attestation.md#wallet-connect-support-walletconnect_support + verified_by: [] + memo_tag_support: + supported: true + proof: proofs/acme-wallet/acme-wallet-self-attestation.md#memo-tag-support-for-transfers-to-exchanges-memo_tag_support + verified_by: [] + + # Network relevant features + utxo_merge_delegation: + supported: false + proof: null + verified_by: [] + + # Party Specific + key_recovery: + supported: true + social_recovery: + supported: true + + # Wallet Capabilities + clear_signing: + supported: true + proof: null + verified_by: [] + assets_supported: ["CC", "USDC (CIP-0056)"] + languages_supported: ["English", "Spanish"] diff --git a/proofs/_TEMPLATE/TEMPLATE-self-attestation.md b/proofs/_TEMPLATE/TEMPLATE-self-attestation.md new file mode 100644 index 0000000..c93e6a6 --- /dev/null +++ b/proofs/_TEMPLATE/TEMPLATE-self-attestation.md @@ -0,0 +1,314 @@ +# โ€” Self-Attestation Evidence + +Wallet name: `your-wallet-name` +Last updated: YYYY-MM-DD + + + + +--- + +## CC support (transfers and holding) `cc_support` + +> Send, receive, and hold Canton Coin. +> +> Suggested test: Send CC to and from the wallet. Record the transaction +> update IDs and optionally provide screenshots of the holding, transfer, and +> party ID. + +- Transaction update ID(s): +- Party ID(s): +- Explorer link(s): +- Screenshot(s) / video: ![holding](./your-wallet-name-self-attestation-images/cc_support-holding.png) +- Notes: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/proofs/_TEMPLATE/TEMPLATE-third-party-attestation.md b/proofs/_TEMPLATE/TEMPLATE-third-party-attestation.md new file mode 100644 index 0000000..b460256 --- /dev/null +++ b/proofs/_TEMPLATE/TEMPLATE-third-party-attestation.md @@ -0,0 +1,408 @@ +# โ€” Third-Party Verification Evidence + +Wallet name: `wallet-name` + + + +--- + +## CC support (transfers and holding) `cc_support` โ€” Verified by + +> Send, receive, and hold Canton Coin. +> +> Suggested test: Send CC to and from the wallet. Record the transaction +> update IDs and optionally provide screenshots of the holding, transfer, and +> party ID. + +Verifier: +Contact: +Date verified: YYYY-MM-DD + +- Transaction update ID(s): +- Party ID(s): +- Explorer link(s): +- Screenshot(s) / video: ![holding](./your-wallet-name-third-party-attestation-images/cc_support-verifier-name-holding.png) +- Notes: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/proofs/dummy-wallet/dummy-wallet-self-attestation.md b/proofs/dummy-wallet/dummy-wallet-self-attestation.md new file mode 100644 index 0000000..8728785 --- /dev/null +++ b/proofs/dummy-wallet/dummy-wallet-self-attestation.md @@ -0,0 +1,85 @@ +# Dummy Wallet โ€” Self-Attestation Evidence + +Wallet name: `dummy-wallet` +Last updated: 2026-06-10 + + + +--- + +## CC support (transfers and holding) `cc_support` + +> Send, receive, and hold Canton Coin. +> +> Suggested test: Send CC to and from the wallet. Record the transaction +> update IDs and optionally provide screenshots of the holding, transfer, and +> party ID. + +- Transaction update ID(s): `1220a1b2c3d4e5f6...dummy-cc-holding`, `1220f6e5d4c3b2a1...dummy-cc-transfer` +- Party ID(s): `dummy-wallet-party-1::1220...` +- Explorer link(s): https://scan.example.com/tx/1220a1b2c3d4e5f6...dummy-cc-holding +- Screenshot(s) / video: ![holding](./dummy-wallet-self-attestation-images/cc_support-holding.png) +- Notes: Round-trip transfer performed on Canton MainNet. + +## Pre-approvals for CC `preapprovals` + +> Auto-accept incoming CC without manual confirmation. Distinct from +> registry_preapprovals (App Specific) below -- the two used to share an id +> but now have different suggested tests. +> +> Suggested test: Enable pre-approvals for CC. Send CC to the wallet address. +> Confirm the transfer was auto-accepted. Record the transaction update IDs +> and optionally provide screenshots of the holding, transfer, and party ID. + +- Transaction update ID(s): `1220b2c3d4e5f6a1...dummy-preapproval` +- Party ID(s): `dummy-wallet-party-1::1220...` +- Explorer link(s): https://scan.example.com/tx/1220b2c3d4e5f6a1...dummy-preapproval +- Screenshot(s) / video: ![auto-accept](./dummy-wallet-self-attestation-images/preapprovals-auto-accept.png) +- Notes: Pre-approval enabled, then an unsolicited CC transfer was auto-accepted with no manual step. + +## CIP-0056 token standard transfer support `cip_0056_transfer` + +> Send and receive any CIP-0056 token, not just CC. +> +> Suggested test: Send a CIP-0056-compatible token (not Canton Coin) to and +> from the wallet. Record the transaction update IDs and optionally provide +> screenshots of the holding, transfer, and party ID. + +- Transaction update ID(s): `1220c3d4e5f6a1b2...dummy-cip0056-transfer` +- Party ID(s): `dummy-wallet-party-1::1220...` +- Explorer link(s): https://scan.example.com/tx/1220c3d4e5f6a1b2...dummy-cip0056-transfer +- Screenshot(s) / video: ![transfer](./dummy-wallet-self-attestation-images/cip_0056_transfer-holding.png) +- Notes: Transferred "Example Token (CIP-0056)" to and from the wallet. + +## CIP-0103 dApp API support `cip_0103_dapp_api` + +> Connect to and sign transactions from a third-party dApp via CIP-0103. +> +> Suggested test: Connect to a dApp using a CIP-0103 connection. Initiate a +> transaction from within the dApp and sign it from the wallet. Confirm the +> successful transaction. Optionally provide screenshots or a video +> recording. + +- Transaction update ID(s): `1220d4e5f6a1b2c3...dummy-dapp-connect` +- Party ID(s): `dummy-wallet-party-1::1220...` +- Explorer link(s): https://scan.example.com/tx/1220d4e5f6a1b2c3...dummy-dapp-connect +- Screenshot(s) / video: ![dapp-connect](./dummy-wallet-self-attestation-images/cip_0103_dapp_api-session.png) +- Notes: Connected to a sample dApp via CIP-0103 and signed a transaction initiated from it. + +## Memo tag support for transfers to exchanges `memo_tag_support` + +> Attach a memo tag to a transfer, recorded under the +> splice.lfdecentralizedtrust.org/reason metadata. +> +> Suggested test: Include a memo tag in an asset transfer (this can be +> combined with the cc_support test). Record the transaction update ID +> showing the memo tag under the splice.lfdecentralizedtrust.org/reason +> metadata in the transaction JSON. + +- Transaction update ID(s): `1220e5f6a1b2c3d4...dummy-memo-tag` +- Party ID(s): `dummy-wallet-party-1::1220...` +- Explorer link(s): https://scan.example.com/tx/1220e5f6a1b2c3d4...dummy-memo-tag +- Screenshot(s) / video: ![memo-tag](./dummy-wallet-self-attestation-images/memo_tag_support-metadata.png) +- Notes: Transaction JSON shows the memo tag under `splice.lfdecentralizedtrust.org/reason`. diff --git a/proofs/dummy-wallet/dummy-wallet-third-party-attestation.md b/proofs/dummy-wallet/dummy-wallet-third-party-attestation.md new file mode 100644 index 0000000..27b42fa --- /dev/null +++ b/proofs/dummy-wallet/dummy-wallet-third-party-attestation.md @@ -0,0 +1,28 @@ +# Dummy Wallet โ€” Third-Party Verification Evidence + +Wallet name: `dummy-wallet` + + + +--- + +## CC support (transfers and holding) `cc_support` โ€” Verified by Blockdaemon + +> Send, receive, and hold Canton Coin. +> +> Suggested test: Send CC to and from the wallet. Record the transaction +> update IDs and optionally provide screenshots of the holding, transfer, and +> party ID. + +Verifier: Blockdaemon +Contact: verification@blockdaemon.example +Date verified: 2026-06-05 + +- Transaction update ID(s): `1220f1e2d3c4b5a6...blockdaemon-verify-cc` +- Party ID(s): `dummy-wallet-party-1::1220...` +- Explorer link(s): https://scan.example.com/tx/1220f1e2d3c4b5a6...blockdaemon-verify-cc +- Screenshot(s) / video: ![holding](./dummy-wallet-third-party-attestation-images/cc_support-blockdaemon-holding.png) +- Notes: Independently sent and received CC using our own party and transaction, confirming the wallet's claim. diff --git a/scripts/generate_table.py b/scripts/generate_table.py new file mode 100644 index 0000000..9a55420 --- /dev/null +++ b/scripts/generate_table.py @@ -0,0 +1,388 @@ +#!/usr/bin/env python3 +""" +Regenerates WALLET_DIRECTORY.md from wallets/_feature_registry.yaml and +wallets/*.yaml. + +Usage: + python3 scripts/generate_table.py + python3 scripts/generate_table.py --wallets-dir examples --out /tmp/demo.md + +No install step, no dependencies -- this file is everything you need to run +it. Just run it with plain python3. (It includes its own small YAML reader +below, scoped to exactly the handful of YAML shapes used by wallets/*.yaml, +so nothing needs to be pip installed.) + +Run this after adding or editing a wallet YAML file and commit the +regenerated WALLET_DIRECTORY.md alongside your change. + +Output is two tables, matching the split in Feature List.md: "Wallet +Overview & Primary Canton Network Features" (Wallet Type, Canton Coin, +Token Standard, Interoperability) and "Other Features" (everything else). +There is no "required vs. optional" distinction -- see the note at the top +of wallets/_feature_registry.yaml. +""" +import argparse +import datetime +import glob +import os +import re + +INDENT = "    " + +# Categories that make up table 1, "Wallet Overview & Primary Canton +# Network Features" -- everything else goes in table 2, "Other Features". +TABLE_1_CATEGORIES = {"Wallet Type", "Canton Coin", "Token Standard", "Interoperability"} + + +# --- Minimal YAML reader ----------------------------------------------- +# +# This repo's wallets/*.yaml and wallets/_feature_registry.yaml files only +# ever use a small, fixed set of YAML shapes: nested mappings (2-space +# indented), block sequences ("- item"), flow sequences ("[a, b]"), +# quoted/unquoted scalars, null/true/false, and ">"-folded blocks (used +# only for suggested_test, which nothing here reads programmatically). This +# reader covers exactly that, so generate_table.py needs nothing beyond the +# Python standard library -- no pip install, no venv, no PEP 668 headaches +# for contributors. It is not a general-purpose YAML parser; don't reach +# for it outside this script. + +def _strip_comment(text): + # '#' starts a comment only at line-start or after whitespace, so it + # doesn't clip unquoted proof URLs like ".../file.md#some-anchor". + in_squote = in_dquote = False + for i, c in enumerate(text): + if c == "'" and not in_dquote: + in_squote = not in_squote + elif c == '"' and not in_squote: + in_dquote = not in_dquote + elif c == "#" and not in_squote and not in_dquote: + if i == 0 or text[i - 1].isspace(): + return text[:i].rstrip() + return text.rstrip() + + +def _parse_scalar(s): + s = s.strip() + if s in ("", "~", "null"): + return None + if s == "true": + return True + if s == "false": + return False + if len(s) >= 2 and s[0] == s[-1] == '"': + return s[1:-1] + if len(s) >= 2 and s[0] == s[-1] == "'": + return s[1:-1] + if re.fullmatch(r"-?\d+", s): + return int(s) + return s + + +def _parse_flow_list(s): + inner = s.strip()[1:-1].strip() + if not inner: + return [] + items, buf, in_quote = [], "", None + for c in inner: + if in_quote: + buf += c + if c == in_quote: + in_quote = None + elif c in "\"'": + in_quote = c + buf += c + elif c == ",": + items.append(_parse_scalar(buf)) + buf = "" + else: + buf += c + if buf.strip() != "": + items.append(_parse_scalar(buf)) + return items + + +def _parse_value(val): + val = val.strip() + if val.startswith("[") and val.endswith("]"): + return _parse_flow_list(val) + if re.match(r"^[|>][+-]?$", val): + return None # block-scalar body, discarded during preprocessing + return _parse_scalar(val) + + +def _split_kv(content): + key, _, val = content.partition(":") + return key.strip(), val.strip() + + +def _preprocess(raw_text): + """Flattens the file into [indent, content] entries, one per + meaningful line, with ">"/"|" block-scalar bodies swallowed whole + (their content is never needed by this script).""" + lines = raw_text.split("\n") + entries = [] + i, n = 0, len(lines) + while i < n: + raw = lines[i] + if raw.strip() == "" or raw.lstrip(" ").startswith("#"): + i += 1 + continue + indent = len(raw) - len(raw.lstrip(" ")) + content = _strip_comment(raw).strip() + if content == "": + i += 1 + continue + entries.append([indent, content]) + if re.match(r"^[^:]+:\s*[|>][+-]?\s*$", content): + i += 1 + while i < n and (lines[i].strip() == "" or len(lines[i]) - len(lines[i].lstrip(" ")) > indent): + i += 1 + continue + i += 1 + return entries + + +def _parse_nodes(entries, pos, indent): + """Parses sibling nodes at exactly `indent` starting at entries[pos] + into a dict or list, based on whether the first sibling is a "- " item.""" + if pos >= len(entries) or entries[pos][0] != indent: + return None, pos + + if entries[pos][1].startswith("- ") or entries[pos][1] == "-": + result = [] + while pos < len(entries) and entries[pos][0] == indent and ( + entries[pos][1].startswith("- ") or entries[pos][1] == "-" + ): + content = entries[pos][1] + rest = content[1:].lstrip() if content != "-" else "" + if rest == "": + pos += 1 + child, pos = _parse_nodes(entries, pos, indent + 2) + result.append(child) + elif ":" in rest and not rest.startswith(("[", '"', "'")): + virtual_indent = indent + 2 + key, val = _split_kv(rest) + item = {key: _parse_value(val)} + pos += 1 + while pos < len(entries) and entries[pos][0] == virtual_indent: + k2, v2 = _split_kv(entries[pos][1]) + if v2 == "": + pos += 1 + child, pos = _parse_nodes(entries, pos, virtual_indent + 2) + item[k2] = child + else: + item[k2] = _parse_value(v2) + pos += 1 + result.append(item) + else: + result.append(_parse_value(rest)) + pos += 1 + return result, pos + + result = {} + while pos < len(entries) and entries[pos][0] == indent: + key, val = _split_kv(entries[pos][1]) + if val == "": + pos += 1 + if pos < len(entries) and entries[pos][0] > indent: + child, pos = _parse_nodes(entries, pos, entries[pos][0]) + result[key] = child + else: + result[key] = None + else: + result[key] = _parse_value(val) + pos += 1 + return result, pos + + +def yaml_load(path): + with open(path) as f: + text = f.read() + entries = _preprocess(text) + if not entries: + return None + value, _ = _parse_nodes(entries, 0, entries[0][0]) + return value + + +# --- Table generation ---------------------------------------------------- + +def load_registry(path): + return yaml_load(path) + + +def load_wallets(wallets_dir): + wallets = [] + for path in sorted(glob.glob(os.path.join(wallets_dir, "*.yaml"))): + base = os.path.basename(path) + if base.startswith("_") or base.upper().startswith("TEMPLATE"): + continue + data = yaml_load(path) + if not data: + continue + data["_source_file"] = os.path.relpath(path) + wallets.append(data) + return sorted(wallets, key=lambda w: w.get("name", "").lower()) + + +def wallet_file_link(wallet): + return f"wallets/{os.path.basename(wallet.get('_source_file', ''))}" + + +def wallet_column_header(wallet): + return f"[{wallet.get('name', '?')}]({wallet_file_link(wallet)})" + + +def render_boolean_cell(feature_id, wallet, self_attested_only=False): + val = (wallet.get("features") or {}).get(feature_id) + if not isinstance(val, dict): + return "โ€”" + + if not val.get("supported"): + reason = val.get("reason") + if reason: + return f"[Not supported]({wallet_file_link(wallet)})" + return "โ€”" + + # self_attested_only features have no proof/verification mechanism -- + # generic wallet capabilities that aren't Canton Network specific, so + # there's nothing to test against the network. Render a plain claim, + # ignoring any proof/verified_by even if present. + if self_attested_only: + return "โœ…" + + proof = val.get("proof") + cell = f"[โœ…]({proof})" if proof else "โœ…" + + verified_by = val.get("verified_by") or [] + confirmed = [v for v in verified_by if v.get("result", "verified") != "unsupported"] + disputed = [v for v in verified_by if v.get("result") == "unsupported"] + + def link_names(entries): + names = [] + for v in entries: + name = v.get("by", "unknown") + vproof = v.get("proof") + names.append(f"[{name}]({vproof})" if vproof else name) + return ", ".join(names) + + if confirmed: + cell += " ๐Ÿ›ก๏ธ" + link_names(confirmed) + if disputed: + cell += " โŒ" + link_names(disputed) + + return cell + + +def render_freetext_cell(feature_id, wallet): + val = (wallet.get("features") or {}).get(feature_id) + if val in (None, "", []): + return "โ€”" + if isinstance(val, list): + return ", ".join(str(v) for v in val) if val else "โ€”" + return str(val) + + +def render_feature_cell(feature, wallet): + if feature["type"] == "freetext": + return render_freetext_cell(feature["id"], wallet) + return render_boolean_cell(feature["id"], wallet, feature.get("self_attested_only", False)) + + +def render_wallet_overview_rows(wallets): + n = len(wallets) + lines = ["| **Wallet Overview** | " + " | ".join([""] * n) + " |"] + + row = [f"{INDENT}Website"] + for w in wallets: + site = w.get("website") + row.append(f"[{site}]({site})" if site else "โ€”") + lines.append("| " + " | ".join(row) + " |") + + row = [f"{INDENT}Added"] + for w in wallets: + added = w.get("added") + row.append(added if added else "โ€”") + lines.append("| " + " | ".join(row) + " |") + + return lines + + +def render_table(features, wallets, include_overview_block): + header = ["Feature"] + [wallet_column_header(w) for w in wallets] + lines = ["| " + " | ".join(header) + " |", + "|" + "|".join(["---"] * len(header)) + "|"] + + if include_overview_block: + lines.extend(render_wallet_overview_rows(wallets)) + + current_category = None + for feature in features: + if feature["category"] != current_category: + current_category = feature["category"] + lines.append(f"| **{current_category}** | " + " | ".join([""] * len(wallets)) + " |") + row = [f"{INDENT}{feature['name']}"] + for w in wallets: + row.append(render_feature_cell(feature, w)) + lines.append("| " + " | ".join(row) + " |") + + return "\n".join(lines) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--registry", default="wallets/_feature_registry.yaml") + parser.add_argument("--wallets-dir", default="wallets") + parser.add_argument("--out", default="WALLET_DIRECTORY.md") + args = parser.parse_args() + + registry = load_registry(args.registry) + all_features = registry.get("features", []) + table_1_features = [f for f in all_features if f["category"] in TABLE_1_CATEGORIES] + table_2_features = [f for f in all_features if f["category"] not in TABLE_1_CATEGORIES] + wallets = load_wallets(args.wallets_dir) + + now = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC") + + if wallets: + table_1_md = render_table(table_1_features, wallets, include_overview_block=True) + table_2_md = render_table(table_2_features, wallets, include_overview_block=False) + else: + placeholder = "_No wallets listed yet. See [CONTRIBUTING.md](./CONTRIBUTING.md) to apply._" + table_1_md = placeholder + table_2_md = "_No wallets listed yet._" + + body = f""" + +# Wallet Directory + +Source of truth for Canton Network wallet providers' supported features and +assets, per the Wallet Directory Program (see [CONTRIBUTING.md](./CONTRIBUTING.md)). +Last generated {now}. + +**Legend:** โœ… self-attested -- click through to the evidence ยท ๐Ÿ›ก๏ธ *name* verified by an +independent third party -- click the name for their evidence ยท โŒ *name* found this claim to +be unsupported, contradicting the wallet -- click the name for their evidence ยท a linked +"Not supported" means the wallet has said no with a reason ยท +โ€” no claim made either way. + +## Wallet Overview & Primary Canton Network Features + +{table_1_md} + +## Other Features + +{table_2_md} +""" + + with open(args.out, "w") as f: + f.write(body) + print(f"Wrote {args.out} ({len(wallets)} wallet(s) from {args.wallets_dir}/)") + + +if __name__ == "__main__": + main() diff --git a/wallets/TEMPLATE.yaml b/wallets/TEMPLATE.yaml new file mode 100644 index 0000000..6ebfddf --- /dev/null +++ b/wallets/TEMPLATE.yaml @@ -0,0 +1,308 @@ +# Copy this file to wallets/.yaml (lowercase, hyphenated, +# e.g. wallets/acme-wallet.yaml) and fill in every field below. See +# CONTRIBUTING.md for the full walkthrough (evidence files, verification, +# regenerating the summary page). + +name: "Your Wallet Name" +wallet-name: your-wallet-name # lowercase, hyphenated -- must match proofs// +website: "https://example.com" +contact: "you@example.com" # email or Slack handle +added: null # YYYY-MM-DD + +# --- Features --- +# You don't need to include every id -- omitted ids render as "โ€”" on the +# Wallet Directory page. +# +# freetext features (e.g. wallet_model, assets_supported): give a string or +# a list of strings directly. +# +# boolean features (e.g. cc_support, clear_signing): give a small object: +# supported: true | false +# reason: short text explaining a `false`, e.g. "not applicable to +# our product" or "on our roadmap for Q3". Optional, only +# used when supported is false -- leave null or omit +# otherwise. See CONTRIBUTING.md -> "Marking a feature as +# not supported". +# proof: link to your evidence heading in +# proofs/your-wallet-name/your-wallet-name-self-attestation.md, +# e.g. proofs/your-wallet-name/your-wallet-name-self-attestation.md#cc-support-transfers-and-holding-cc_support +# Leave null if you haven't put together evidence yet. +# verified_by: list of independent verifications. Leave as [] until a +# third party verifies this claim (see CONTRIBUTING.md -> +# "Third-party verification"). Each entry: +# - by: Verifier name +# date: "YYYY-MM-DD" +# result: verified | unsupported +# proof: link to their heading in +# proofs/your-wallet-name/your-wallet-name-third-party-attestation.md +# +# A few boolean features (generic wallet capabilities that aren't Canton +# Network specific -- see "self_attested_only" in +# wallets/_feature_registry.yaml) have no proof/verification mechanism at +# all. For those, just give `supported` (and `reason` if false) -- omit +# `proof` and `verified_by` entirely. + + +features: + # Wallet Type + # Who the wallet is built for. + wallet_model: [] # any of: Retail, Enterprise + # Who holds the private keys (custodial and/or self-custodial). + custody_model: null # Custodial and / or Self-custodial + # Mobile, browser, desktop, browser extension, hardware. + form_factor: [] # any of: mobile, browser, desktop, browser-extension, hardware + # Self-hosted, SaaS, hybrid, or on-prem. + deployment_model: [] # any of: self-hosted, saas, hybrid, on-prem + + # Canton Coin + # Send, receive, and hold Canton Coin. + # Suggested test: Send CC to and from the wallet. Record the transaction + # update IDs and optionally provide screenshots of the holding, transfer, + # and party ID. + cc_support: + supported: false + # reason: "" + proof: null + verified_by: [] + + # Auto-accept incoming CC without manual confirmation. Distinct from + # registry_preapprovals (App Specific) below. + # Suggested test: Enable pre-approvals for CC. Send CC to the wallet + # address. Confirm the transfer was auto-accepted. Record the transaction + # update IDs and optionally provide screenshots of the holding, transfer, + # and party ID. + preapprovals: + supported: false + # reason: "" + proof: null + verified_by: [] + + # Token Standard + # Send and receive any CIP-0056 token, not just CC. + # Suggested test: Send a CIP-0056-compatible token (not Canton Coin) to and + # from the wallet. Record the transaction update IDs and optionally provide + # screenshots of the holding, transfer, and party ID. + cip_0056_transfer: + supported: false + # reason: "" + proof: null + verified_by: [] + + ## Support for the allocation half of the CIP-0056 standard, distinct from + ## plain transfers. + ## Suggested test: Create and settle a CIP-0056 allocation (distinct from a + ## plain transfer). Record the transaction update ID(s) for the allocation + ## and its settlement, and optionally provide screenshots of the allocation + ## before and after settlement. + # cip_0056_allocation: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + ## Support for CIP-112 v2 tokens. + ## Suggested test: Send or hold a CIP-112 v2 token in the wallet. Record the + ## transaction update ID and optionally provide screenshots of the holding + ## and transfer, noting the CIP-112 version in the token metadata. + # cip_112_v2: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + # Interoperability + # Connect to and sign transactions from a third-party dApp via CIP-0103. + # Suggested test: Connect to a dApp using a CIP-0103 connection. Initiate + # a transaction from within the dApp and sign it from the wallet. Confirm + # the successful transaction. Optionally provide screenshots or a video + # recording. + cip_0103_dapp_api: + supported: false + # reason: "" + proof: null + verified_by: [] + + # Connect to and sign transactions from a third-party dApp via Wallet + # Connect. + # Suggested test: Connect to a dApp using Wallet Connect. Initiate a + # transaction from within the dApp and sign it from the wallet. Confirm + # the successful transaction. Optionally provide screenshots or a video + # recording. + walletconnect_support: + supported: false + # reason: "" + proof: null + verified_by: [] + + # Attach a memo tag to a transfer, recorded under the + # splice.lfdecentralizedtrust.org/reason metadata. + # Suggested test: Include a memo tag in an asset transfer (this can be + # combined with the cc_support test). Record the transaction update ID + # showing the memo tag under the splice.lfdecentralizedtrust.org/reason + # metadata in the transaction JSON. + memo_tag_support: + supported: false + # reason: "" + proof: null + verified_by: [] + + # App Specific + ## Auto-accept incoming registry-issued (non-CC) assets. Distinct from + ## Canton Coin preapprovals. + ## Suggested test: Register pre-approvals for a DA Registry issued asset. + ## Send the asset to the wallet address. Confirm the asset was + ## auto-accepted. Record the transaction update IDs and optionally provide + ## screenshots of the holding, transfer, and party ID. + # registry_preapprovals: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + ## Integration with the Wallet Gateway signing driver. + ## Suggested test: Integrate with the Wallet Gateway signing driver and + ## demonstrate a signed transaction routed through it. Link to the signing + ## driver. Optionally provide a screenshot or log excerpt + ## showing the wallet handling the signing request. + # wallet_gateway_signing_driver: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + # Party Specific + + ## Free-text description of how keys are generated (e.g. seed phrase, HSM, + ## MPC). + # key_generation_method: "Seed phrase (BIP-39)" + + ## Ability to recover a lost key. Not Canton Network specific -- no proof + ## needed. + # key_recovery: + # supported: true + # # reason: "" + + ## Recovery via trusted guardians/contacts rather than a seed phrase. Not + ## Canton Network specific -- no proof needed. + # social_recovery: + # supported: true + # # reason: "" + + ## Support for multiple addresses/accounts within one wallet. Not Canton + ## Network specific -- no proof needed. + # multi_address: + # supported: true + # # reason: "" + + ## Support for parties whose authority is distributed across multiple + ## signers. + # distributed_parties: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + + # Network relevant features + + ## The wallet enables the merge delegation contract so UTXOs can be + ## consolidated on-chain, reducing UTXO bloat. + ## Suggested test: Enable the merge delegation contract in the wallet. + ## Witness a UTXO consolidation contract on-chain for CC (sender & receiver + ## are the same party). + # utxo_merge_delegation: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + # Transaction Signing + ## Configurable approval policies before a transaction is signed. Not + ## Canton Network specific -- no proof needed. + # policy_workflows: + # supported: true + # # reason: "" + + ## MPC-based threshold signing (as distinct from blockchain-native + ## multi-sig). Not Canton Network specific -- no proof needed. + # threshold_signature_scheme: + # supported: true + # # reason: "" + + ## Support for hardware signing devices. + # hardware_wallet_support: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + + # Wallet Capabilities + + ## Compliance capabilities/certifications the wallet supports. Not Canton + ## Network specific -- no proof needed. + # compliance: + # supported: true + # # reason: "" + + ## Validator hosting, self-hosted or bring-your-own-validator (BYOV). Not + ## Canton Network specific -- no proof needed. + # validator_hosting: + # supported: true + # # reason: "" + + ## Tokenization capabilities the wallet supports. + # tokenization: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + ## Claiming or minting a reward (e.g. a validator or app reward). + ## Suggested test: Demonstrate the wallet claiming or minting a reward (e.g. + ## a validator or app reward). Provide the transaction update ID of the + ## reward-minting event and a screenshot of the resulting holding. + # reward_minting: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + ## Generate a transfer / proof-of-transfer object for a completed + ## transaction. + ## Suggested test: Generate a transfer/proof-of-transfer object for a + ## completed transaction. Provide the transaction update ID and a screenshot + ## or export of the proof object. + # transfer_proof: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + ## The wallet displays human-readable transaction details (not raw + ## hex/opaque data) before signing. + ## Suggested test: Show the wallet displaying human-readable transaction + ## details (not raw hex/opaque data) before signing. Provide a screenshot of + ## the clear-signing confirmation screen for a real transaction, alongside + ## its transaction update ID. + # clear_signing: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + ## An in-wallet swap between two assets. + ## Suggested test: Perform an in-wallet swap between two assets. Provide the + ## transaction update ID(s) for the swap and a screenshot of balances before + ## and after. + # integrated_swaps: + # supported: true + # # reason: "" + # proof: null + # verified_by: [] + + ## Free-text list of assets the wallet supports. + # assets_supported: ["CC", "USDC (CIP-0056)"] + + ## Free-text list of languages the wallet's UI supports. + # languages_supported: ["English", "Japanese"] \ No newline at end of file diff --git a/wallets/_feature_registry.yaml b/wallets/_feature_registry.yaml new file mode 100644 index 0000000..4118a7d --- /dev/null +++ b/wallets/_feature_registry.yaml @@ -0,0 +1,261 @@ +# Single source of truth for feature IDs, names, categories, and types. +# +# type: boolean -> wallet claims supported: true/false, may attach proof +# and/or third-party verification. +# type: freetext -> descriptive value (string or list), not a proof-backed +# yes/no claim. +# +# self_attested_only: true -> a boolean feature with no proof/verification +# mechanism at all -- generic wallet capabilities that +# aren't Canton Network specific, so there's nothing to +# test against the network. Wallets just set +# supported: true/false (plus reason if false); omit +# proof and verified_by entirely for these ids. + +features: + # --- Wallet Type --- + - id: wallet_model + name: "Retail / Enterprise" + category: "Wallet Type" + type: freetext + + - id: custody_model + name: "Custody Type" + category: "Wallet Type" + type: freetext + + - id: form_factor + name: "Form factor (Mobile / Browser / Desktop / Browser Extension / Hardware)" + category: "Wallet Type" + type: freetext + + - id: deployment_model + name: "Deployment Model (Self-hosted / SaaS / Hybrid)" + category: "Wallet Type" + type: freetext + + # --- Primary Canton Network Features > Canton Coin --- + - id: cc_support + name: "CC support (transfers and holding)" + category: "Canton Coin" + type: boolean + suggested_test: > + Send CC to and from the wallet. Record the transaction update IDs and + optionally provide screenshots of the holding, transfer, and party ID. + + - id: preapprovals + name: "Pre-approvals for CC" + category: "Canton Coin" + type: boolean + suggested_test: > + Enable pre-approvals for CC. Send CC to the wallet address. Confirm + the transfer was auto-accepted. Record the transaction update IDs and + optionally provide screenshots of the holding, transfer, and party ID. + + # --- Primary Canton Network Features > Token Standard --- + - id: cip_0056_transfer + name: "CIP-0056 token standard transfer support" + category: "Token Standard" + type: boolean + suggested_test: > + Send a CIP-0056-compatible token (not Canton Coin) to and from the + wallet. Record the transaction update IDs and optionally provide + screenshots of the holding, transfer, and party ID. + + - id: cip_0056_allocation + name: "CIP-0056 token standard allocation support" + category: "Token Standard" + type: boolean + suggested_test: > + Create and settle a CIP-0056 allocation (distinct from a plain + transfer). Record the transaction update ID(s) for the allocation and + its settlement, and optionally provide screenshots of the allocation + before and after settlement. + + - id: cip_112_v2 + name: "CIP-112 token standard v2 support" + category: "Token Standard" + type: boolean + suggested_test: > + Send or hold a CIP-112 v2 token in the wallet. Record the transaction + update ID and optionally provide screenshots of the holding and transfer, noting + the CIP-112 version in the token metadata. + + # --- Primary Canton Network Features > Interoperability + - id: cip_0103_dapp_api + name: "CIP-0103 dApp API support" + category: "Interoperability" + type: boolean + suggested_test: > + Connect to a dApp using a CIP-0103 connection. Initiate a transaction + from within the dApp and sign it from the wallet. Confirm the + successful transaction. Optionally provide screenshots or a video + recording. + + - id: walletconnect_support + name: "Wallet Connect support" + category: "Interoperability" + type: boolean + suggested_test: > + Connect to a dApp using Wallet Connect. Initiate a transaction from + within the dApp and sign it from the wallet. Confirm the successful + transaction. Optionally provide screenshots or a video recording. + + - id: memo_tag_support + name: "Memo tag support for transfers to exchanges" + category: "Interoperability" + type: boolean + suggested_test: > + Include a memo tag in an asset transfer (this can be combined with + the cc_support test). Record the transaction update ID showing the + memo tag under the splice.lfdecentralizedtrust.org/reason metadata in + the transaction JSON. + + # --- Other Features > App Specific --- + # NOTE: this used to share the "preapprovals" id with the Canton Coin + # entry above on the theory that one combined test covered both. Their + # suggested_test steps have since diverged (this one is registry-asset + # specific), so it's its own id -- a wallet claiming both fills in both + # `preapprovals` and `registry_preapprovals` separately. + - id: registry_preapprovals + name: "Pre-approvals for DA Registry issued assets" + category: "App Specific" + type: boolean + suggested_test: > + Register pre-approvals for a DA Registry issued asset. Send the asset to the wallet + address. Confirm the asset was auto-accepted. Record the transaction + update IDs and optionally provide screenshots of the holding, transfer, + and party ID. + + - id: wallet_gateway_signing_driver + name: "Wallet Gateway signing driver" + category: "App Specific" + type: boolean + suggested_test: > + Integrate with the Wallet Gateway signing driver and demonstrate a + signed transaction routed through it. Link to the signing driver. + Record the transaction update ID and the Wallet Gateway + request/session ID, and optinoally provide a screenshot or log + excerpt showing the driver handling the signing request. + + # --- Other Features > Party Specific --- + - id: key_generation_method + name: "Key generation method" + category: "Party Specific" + type: freetext + + - id: key_recovery + name: "Key recovery" + category: "Party Specific" + type: boolean + self_attested_only: true + + - id: social_recovery + name: "Social recovery" + category: "Party Specific" + type: boolean + self_attested_only: true + + - id: multi_address + name: "Multi-address / account" + category: "Party Specific" + type: boolean + self_attested_only: true + + - id: distributed_parties + name: "Distributed parties" + category: "Party Specific" + type: boolean + + # --- Other Features > Network relevant features --- + - id: utxo_merge_delegation + name: "UTXO merge delegation contract enabled" + category: "Network relevant features" + type: boolean + suggested_test: > + Enable the merge delegation contract in the wallet. Witness a UTXO + consolidation contract on-chain for CC (sender & receiver are the + same party). + + # --- Other Features > Transaction Signing --- + - id: policy_workflows + name: "Policy workflows" + category: "Transaction Signing" + type: boolean + self_attested_only: true + + - id: threshold_signature_scheme + name: "Threshold Signature Scheme" + category: "Transaction Signing" + type: boolean + self_attested_only: true + + - id: hardware_wallet_support + name: "Hardware wallet support" + category: "Transaction Signing" + type: boolean + + # --- Other Features > Wallet Capabilities --- + - id: compliance + name: "Compliance" + category: "Wallet Capabilities" + type: boolean + self_attested_only: true + + - id: validator_hosting + name: "Validator hosting (self-hosted / BYOV)" + category: "Wallet Capabilities" + type: boolean + self_attested_only: true + + - id: tokenization + name: "Tokenization" + category: "Wallet Capabilities" + type: boolean + + - id: reward_minting + name: "Reward minting" + category: "Wallet Capabilities" + type: boolean + suggested_test: > + Demonstrate the wallet claiming or minting a reward (e.g., a + validator or app reward). Provide the transaction update ID of the + reward-minting event and a screenshot of the resulting holding. + + - id: transfer_proof + name: "Transfer object / proof of transfer support" + category: "Wallet Capabilities" + type: boolean + suggested_test: > + Generate a transfer/proof-of-transfer object for a completed + transaction. Provide the transaction update ID and a screenshot + or export of the proof object. + + - id: clear_signing + name: "Clear signing" + category: "Wallet Capabilities" + type: boolean + suggested_test: > + Show the wallet displaying human-readable transaction details (not + raw hex/opaque data) before signing. Provide a screenshot of the + clear-signing confirmation screen for a real transaction, alongside + its transaction update ID. + + - id: integrated_swaps + name: "Integrated swaps" + category: "Wallet Capabilities" + type: boolean + suggested_test: > + Perform an in-wallet swap between two assets. Provide the + transaction update ID(s) for the swap and a screenshot of balances + before and after. + + - id: assets_supported + name: "Assets supported" + category: "Wallet Capabilities" + type: freetext + + - id: languages_supported + name: "Languages supported" + category: "Wallet Capabilities" + type: freetext \ No newline at end of file diff --git a/wallets/dummy-wallet.yaml b/wallets/dummy-wallet.yaml new file mode 100644 index 0000000..2801ba6 --- /dev/null +++ b/wallets/dummy-wallet.yaml @@ -0,0 +1,82 @@ +# DUMMY SUBMISSION -- created to demonstrate WALLET_DIRECTORY.md rendering. +# Not a real wallet provider. Safe to delete once you've seen the table +# populate, or leave it in place as a live reference for future +# contributors (mirrors examples/acme-wallet.yaml with a real wallets/ +# entry + matching proofs/ folder). + +name: "Dummy Wallet" +wallet-name: dummy-wallet +website: "https://example.com/dummy-wallet" +contact: "dummy-team@example.com" +added: "2026-06-10" +slack_party_scaling_channel_joined: true + +features: + # Wallet Type + wallet_model: [retail, enterprise] + custody_model: self-custodial + form_factor: [browser, mobile] + deployment_model: [saas, hybrid] + + # Canton Coin + cc_support: + supported: true + proof: proofs/dummy-wallet/dummy-wallet-self-attestation.md#cc-support-transfers-and-holding-cc_support + verified_by: + - by: Blockdaemon + date: "2026-06-05" + result: verified + proof: proofs/dummy-wallet/dummy-wallet-third-party-attestation.md#cc-support-transfers-and-holding-cc_support-verified-by-blockdaemon + preapprovals: + supported: true + proof: proofs/dummy-wallet/dummy-wallet-self-attestation.md#pre-approvals-for-cc-preapprovals + verified_by: [] + + # Token Standard + cip_0056_transfer: + supported: true + proof: proofs/dummy-wallet/dummy-wallet-self-attestation.md#cip-0056-token-standard-transfer-support-cip_0056_transfer + verified_by: [] + cip_112_v2: + supported: true + proof: null + verified_by: [] + + # Interoperability + cip_0103_dapp_api: + supported: true + proof: proofs/dummy-wallet/dummy-wallet-self-attestation.md#cip-0103-dapp-api-support-cip_0103_dapp_api + verified_by: [] + walletconnect_support: + supported: false + reason: "Not yet implemented; CIP-0103 is used today for dApp connections." + proof: null + verified_by: [] + memo_tag_support: + supported: true + proof: proofs/dummy-wallet/dummy-wallet-self-attestation.md#memo-tag-support-for-transfers-to-exchanges-memo_tag_support + verified_by: [] + + # Network relevant features + utxo_merge_delegation: + supported: false + proof: null + verified_by: [] + + # Party Specific + key_recovery: + supported: true + + # Transaction Signing + hardware_wallet_support: + supported: true + proof: null + verified_by: [] + + # Wallet Capabilities + clear_signing: + supported: true + proof: null + verified_by: [] + assets_supported: ["CC", "USDC (CIP-0056)", "Example Token (CIP-0056)"] + languages_supported: ["English", "German"] From 4132bb67b8736d160c889a47bacdd1ed2530c9c1 Mon Sep 17 00:00:00 2001 From: David Richards Date: Wed, 8 Jul 2026 12:04:30 +0100 Subject: [PATCH 2/2] Fix CI: drop the 'Last generated' timestamp from generated output The verify-wallet-directory.yml check regenerates WALLET_DIRECTORY.md and diffs it against the committed version to catch staleness. But the file embedded a wall-clock 'Last generated {timestamp}' line, so a fresh regeneration in CI would always differ from whatever was committed by the minute -- the check could never pass, regardless of whether the actual data was in sync. Removing the timestamp makes the output a pure, deterministic function of wallets/*.yaml + the registry, which is what the diff check actually needs. Also drops the now-unused datetime import (and its deprecation warning). --- WALLET_DIRECTORY.md | 1 - scripts/generate_table.py | 4 ---- 2 files changed, 5 deletions(-) diff --git a/WALLET_DIRECTORY.md b/WALLET_DIRECTORY.md index fad3d6d..57e5df3 100644 --- a/WALLET_DIRECTORY.md +++ b/WALLET_DIRECTORY.md @@ -8,7 +8,6 @@ Source of truth for Canton Network wallet providers' supported features and assets, per the Wallet Directory Program (see [CONTRIBUTING.md](./CONTRIBUTING.md)). -Last generated 2026-07-08 10:34 UTC. **Legend:** โœ… self-attested -- click through to the evidence ยท ๐Ÿ›ก๏ธ *name* verified by an independent third party -- click the name for their evidence ยท โŒ *name* found this claim to diff --git a/scripts/generate_table.py b/scripts/generate_table.py index 9a55420..b6db435 100644 --- a/scripts/generate_table.py +++ b/scripts/generate_table.py @@ -22,7 +22,6 @@ of wallets/_feature_registry.yaml. """ import argparse -import datetime import glob import os import re @@ -342,8 +341,6 @@ def main(): table_2_features = [f for f in all_features if f["category"] not in TABLE_1_CATEGORIES] wallets = load_wallets(args.wallets_dir) - now = datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M UTC") - if wallets: table_1_md = render_table(table_1_features, wallets, include_overview_block=True) table_2_md = render_table(table_2_features, wallets, include_overview_block=False) @@ -362,7 +359,6 @@ def main(): Source of truth for Canton Network wallet providers' supported features and assets, per the Wallet Directory Program (see [CONTRIBUTING.md](./CONTRIBUTING.md)). -Last generated {now}. **Legend:** โœ… self-attested -- click through to the evidence ยท ๐Ÿ›ก๏ธ *name* verified by an independent third party -- click the name for their evidence ยท โŒ *name* found this claim to