451 lines
21 KiB
Markdown
451 lines
21 KiB
Markdown
|
|
# Theseus integrated BCH wallet — design
|
||
|
|
|
||
|
|
Companion to [`ROADMAP-identity-wallet.md`](ROADMAP-identity-wallet.md) Strand B
|
||
|
|
and [`DESIGN-password-manager.md`](DESIGN-password-manager.md) (same crypto
|
||
|
|
discipline, distinct purpose subtree). Same shape as the password design doc:
|
||
|
|
threat model → permission model → API → UI → sequencing → open questions.
|
||
|
|
|
||
|
|
The reference point is deliberate: **MetaMask in Brave.** A wallet the user
|
||
|
|
already trusts, injected as `window.bcnr` into every page, permissioned per
|
||
|
|
origin, and additionally exposed to *outside* dApps via WizardConnect so a
|
||
|
|
mobile-only site can pair Theseus like a hardware wallet.
|
||
|
|
|
||
|
|
Everything below is chipnet only until explicitly directed otherwise.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 0. Scope
|
||
|
|
|
||
|
|
**In:**
|
||
|
|
- `window.bcnr` provider injected on every page (read-only immediately;
|
||
|
|
permissioned methods gated per origin).
|
||
|
|
- The user's wallet lives in Theseus. No external key material.
|
||
|
|
- Modal-confirmed operations: `signMessage`, `sendPayment`, `registerName`,
|
||
|
|
`updateName`, `transferName`.
|
||
|
|
- WizardConnect **responder**: dApps outside Theseus (or inside it) can pair
|
||
|
|
via `wiz://` URI + QR to sign transactions with the Theseus wallet.
|
||
|
|
- Settings → Wallet section: connected sites, per-site/per-method revoke,
|
||
|
|
transaction history.
|
||
|
|
- Reuse of the already-shipped `BuiltInWallet`
|
||
|
|
([Argus/src/lib/wallet-web.js:BuiltInWallet](../Argus/src/lib/wallet-web.js))
|
||
|
|
as the signing core.
|
||
|
|
- Reuse of the already-shipped registration/update flow
|
||
|
|
([Argus/src/lib/register-tx.js](../Argus/src/lib/register-tx.js) +
|
||
|
|
[Argus/src/lib/registrar.js](../Argus/src/lib/registrar.js)).
|
||
|
|
|
||
|
|
**Out (deliberate, deferred, or handled elsewhere):**
|
||
|
|
- Ethereum / Solana / other-chain. BCH only in this strand.
|
||
|
|
- Hardware-wallet abstraction (Ledger/Trezor). Phase 4 conversation.
|
||
|
|
- WalletConnect v2. Explicitly dropped 2026-07-27 in favour of WizardConnect
|
||
|
|
(see [`Decentralized.DNS/REGISTRATION-STATUS.md`](../Decentralized.DNS/REGISTRATION-STATUS.md)).
|
||
|
|
We do not want a Reown project ID as a dependency.
|
||
|
|
- CashConnect V0. Cannot mint name certificates by design
|
||
|
|
([`REGISTRATION-STATUS.md`](../Decentralized.DNS/REGISTRATION-STATUS.md) §
|
||
|
|
*CashConnect cannot mint*).
|
||
|
|
- Nostr messenger (NIP-07 client for arbitrary Nostr apps). Separate purpose
|
||
|
|
subtree, Strand B.5 in the roadmap, not this design.
|
||
|
|
- Multi-account UX. One account per Theseus profile in v1.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 1. Threat model
|
||
|
|
|
||
|
|
The provider becomes malware infrastructure the moment its permission model
|
||
|
|
fails. Three attack surfaces to lock down:
|
||
|
|
|
||
|
|
### 1.1 Malicious page reading the provider surface
|
||
|
|
|
||
|
|
Every loaded page sees `window.bcnr`. It must not be possible to enumerate
|
||
|
|
addresses, balances, or names without an explicit user grant.
|
||
|
|
|
||
|
|
- **Read-only methods** (`resolveName`, `getBcnrTlds`, `isRegistered`) are
|
||
|
|
chain queries — they leak nothing about the user. No permission.
|
||
|
|
- **All identifying methods** (`getAccounts`, `signMessage`, `sendPayment`,
|
||
|
|
`registerName`, `updateName`, `transferName`) require an origin permission
|
||
|
|
that started with a modal.
|
||
|
|
- **`getAccounts` returns `[]` for un-granted origins**, not a rejection.
|
||
|
|
Rejection is a signal; empty is opaque.
|
||
|
|
|
||
|
|
### 1.2 Compromised page after permission granted
|
||
|
|
|
||
|
|
An origin can be captured (XSS, subdomain takeover, malicious ad on a trusted
|
||
|
|
host) after the user granted a permission. Assume this.
|
||
|
|
|
||
|
|
- **Per-method scope**, not just per-origin. Granting "sign message" does not
|
||
|
|
grant "send payment". A page has to earn each modal separately.
|
||
|
|
- **`sendPayment` has a per-approval amount cap.** Approving `example.bch` for
|
||
|
|
payments does not approve unlimited spending — the modal shows an amount, and
|
||
|
|
a bigger amount re-prompts. "Approve unlimited" is not a UI option.
|
||
|
|
- **Every mint / update / transfer is modal-confirmed every time.** On-chain
|
||
|
|
writes are irrevocable; there is no "trust for the session" mode.
|
||
|
|
|
||
|
|
### 1.3 Origin confusion (phishing, homoglyph, subdomain)
|
||
|
|
|
||
|
|
- Origin is bound to the **eTLD+1 via the public suffix list snapshot** shipped
|
||
|
|
with Theseus (same rule as password autofill, A.2 in the roadmap). No runtime
|
||
|
|
fetch, no third-party dependency.
|
||
|
|
- **BCNR names get a distinct origin tier** in the confirmation modal:
|
||
|
|
`alice.bch` is a first-class origin (backed by an on-chain certificate),
|
||
|
|
`alice.com` is a second-class origin (registrar-leased). The modal shows the
|
||
|
|
distinction so the user knows what they're trusting.
|
||
|
|
- **The recipient's cash address is shown verbatim in the confirmation.** If
|
||
|
|
the address changes between two confirmations from the same origin during one
|
||
|
|
session, a red warning banner appears — "the address this site is asking you
|
||
|
|
to sign for has changed since your last approval."
|
||
|
|
|
||
|
|
### 1.4 What we are NOT defending against
|
||
|
|
|
||
|
|
- A user manually copying their recovery phrase into a phishing site. No wallet
|
||
|
|
can defend against this and pretending otherwise misleads users.
|
||
|
|
- A user who unlocks the wallet on a shared machine and walks away. Auto-lock
|
||
|
|
helps but is not a substitute.
|
||
|
|
- Compromise of Theseus itself (RCE, malicious extension, keylogger). The
|
||
|
|
password vault and the wallet share the same threat model here — game over.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 2. Permission model
|
||
|
|
|
||
|
|
### 2.1 Per-origin, per-method, per-limit
|
||
|
|
|
||
|
|
Stored in `<userData>/wallet-permissions.json`:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"silentmode.bch": {
|
||
|
|
"grantedAt": 1728000000000,
|
||
|
|
"methods": {
|
||
|
|
"signMessage": { "grants": 12 },
|
||
|
|
"sendPayment": { "capSats": 100000, "used": 45000, "grants": 3 },
|
||
|
|
"registerName": { "grants": 1 }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- `capSats` is the **per-approval** cap the site was granted, not a cumulative
|
||
|
|
budget. A payment above it re-prompts with a new modal.
|
||
|
|
- `used` and `grants` are for the Settings audit trail. The user sees "this
|
||
|
|
site has spent X and signed Y messages" — accountability.
|
||
|
|
- **No wildcard origins.** Every subdomain is a separate grant.
|
||
|
|
|
||
|
|
### 2.2 First-use flow
|
||
|
|
|
||
|
|
1. Page calls `bcnr.requestAccount()` (or any permissioned method).
|
||
|
|
2. Theseus renders a **connect modal** as a `WebContentsView` overlay (same
|
||
|
|
pattern as popover, engine-picker, downloads).
|
||
|
|
3. Modal shows:
|
||
|
|
- Origin, with the BCNR-vs-ICANN badge.
|
||
|
|
- What is being asked (specific method, specific parameters).
|
||
|
|
- "Connect to this site with account `bchtest:qxxx…yyy`" (the wallet's
|
||
|
|
receive address 0, truncated with copy-full option).
|
||
|
|
- **Two buttons**: `Approve once` (single-shot, no permission stored) and
|
||
|
|
`Approve for this site` (grant persisted).
|
||
|
|
4. On approve, the permission is written and the promise resolves.
|
||
|
|
5. On reject, the promise resolves with the same "empty result" pattern as an
|
||
|
|
un-granted origin (no distinguishing signal to the page).
|
||
|
|
|
||
|
|
### 2.3 Revocation
|
||
|
|
|
||
|
|
Settings > Wallet > Connected sites. Table:
|
||
|
|
`origin | granted | last used | scopes | [revoke]`. Per-scope revoke checkboxes,
|
||
|
|
plus a nuclear "revoke all". Revocation clears `wallet-permissions.json` and
|
||
|
|
takes effect on the origin's next call (no restart needed).
|
||
|
|
|
||
|
|
### 2.4 What the provider CAN'T do
|
||
|
|
|
||
|
|
- Read the vault, seed, or any other purpose subtree.
|
||
|
|
- Sign anything without a modal.
|
||
|
|
- Enumerate connected sites (that's a settings-scope API, not page-scope).
|
||
|
|
- Watch the wallet balance across origins (each origin sees only its own
|
||
|
|
granted accounts — usually one).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 3. `window.bcnr` provider — the API
|
||
|
|
|
||
|
|
Injected by a preload extension registered via
|
||
|
|
`session.defaultSession.setPreloads(...)` so it is present on every page. The
|
||
|
|
preload uses `contextBridge.exposeInMainWorld("bcnr", …)` and forwards to main
|
||
|
|
via IPC on a dedicated `bcnr:*` channel.
|
||
|
|
|
||
|
|
### 3.1 Read-only (no permission)
|
||
|
|
|
||
|
|
```ts
|
||
|
|
bcnr.resolveName(name: string): Promise<Entry | null>
|
||
|
|
bcnr.isRegistered(name: string): Promise<boolean>
|
||
|
|
bcnr.getBcnrTlds(): Promise<string[]> // ['bch','p2p','nav',...]
|
||
|
|
bcnr.getRecordVersion(name: string): Promise<number> // for cache invalidation
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.2 Identity (permissioned; first call → modal)
|
||
|
|
|
||
|
|
```ts
|
||
|
|
bcnr.requestAccount(): Promise<string | null> // returns cashaddr or null
|
||
|
|
bcnr.getAccounts(): Promise<string[]> // [] if not granted
|
||
|
|
bcnr.signMessage({ message: string, address?: string, userPrompt?: string }):
|
||
|
|
Promise<{ signature: string, address: string }> // BIP-137
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.3 Payments (permissioned; per-amount modal)
|
||
|
|
|
||
|
|
```ts
|
||
|
|
bcnr.sendPayment({
|
||
|
|
to: string, // cashaddr OR BCNR name resolvable to an `a` record
|
||
|
|
amountSats: bigint,
|
||
|
|
memo?: string,
|
||
|
|
userPrompt?: string,
|
||
|
|
}): Promise<{ txid: string }>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.4 Name operations (each modal-confirmed)
|
||
|
|
|
||
|
|
```ts
|
||
|
|
bcnr.registerName({ name: string, records?: Record<string,string>, userPrompt?: string }):
|
||
|
|
Promise<{ txid: string, category: string }>
|
||
|
|
bcnr.updateName({ name: string, records: Record<string,string>, userPrompt?: string }):
|
||
|
|
Promise<{ txid: string }>
|
||
|
|
bcnr.transferName({ name: string, toAddress: string, userPrompt?: string }):
|
||
|
|
Promise<{ txid: string }>
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.5 Events
|
||
|
|
|
||
|
|
```ts
|
||
|
|
bcnr.on('accountsChanged', (accounts: string[]) => void)
|
||
|
|
bcnr.on('permissionsRevoked', (methods: string[]) => void)
|
||
|
|
bcnr.on('lock', () => void) // wallet auto-locked
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3.6 Match with WizardConnect / WC2 shape where cheap
|
||
|
|
|
||
|
|
`signMessage` returns `{signature, address}` — same shape as
|
||
|
|
`bch_signMessage` in the wc2-bch-bcr namespace (see
|
||
|
|
[Parameters/WalletConnect/WalletConnect_integration_reference.md](../../Deviant/Parameters/WalletConnect/WalletConnect_integration_reference.md)
|
||
|
|
§4.2). Pages that already speak WC2 can adapt with a two-line shim.
|
||
|
|
|
||
|
|
We do NOT re-expose the WC2 wire format (`stringify` with `<Uint8Array: 0x..>`
|
||
|
|
markers) at the provider layer — that is a WC2 transport concern, not a
|
||
|
|
provider API concern. The provider takes plain values; the WizardConnect
|
||
|
|
responder (below) is where the wire format matters.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 4. WizardConnect responder
|
||
|
|
|
||
|
|
The MetaMask analogue is "the wallet is here in the browser." The extra thing
|
||
|
|
we get from WizardConnect is "a dApp on another device — a mobile site, a
|
||
|
|
desktop app — can pair with the Theseus wallet by scanning a `wiz://` QR." This
|
||
|
|
is exactly the responder side of what Deviant is finishing
|
||
|
|
([`Parameters/WalletConnect/WIZARDCONNECT-INTEGRATION-GUIDE.md`](../../Deviant/Parameters/WalletConnect/WIZARDCONNECT-INTEGRATION-GUIDE.md)),
|
||
|
|
adapted to Theseus.
|
||
|
|
|
||
|
|
### 4.1 Wallet-side pieces
|
||
|
|
|
||
|
|
Same shape as Deviant:
|
||
|
|
|
||
|
|
- `@wizardconnect/wallet@0.2.2` for the SDK; already tested against the
|
||
|
|
0.2.4-core wire protocol. `nostr-tools` and `ws` are already in Theseus's
|
||
|
|
deps (verified 2026-08-13).
|
||
|
|
- A **`WalletAdapter`** wrapping Theseus's `BuiltInWallet`. Theseus's wallet
|
||
|
|
is sync-in-main-process behind the unlock modal, which is simpler than
|
||
|
|
Deviant's async biometric-KeyStore case:
|
||
|
|
- `walletName: "Theseus"`, `walletIcon: <shipped png>`
|
||
|
|
- `getRelayPrivateKey(uri): Uint8Array` = `sha256(sha256(utf8(uri)))`
|
||
|
|
(identical to Deviant's implementation — deterministic per URI so a
|
||
|
|
reconnect keeps the same Nostr identity, different dApps cannot
|
||
|
|
correlate; matches `@wizardconnect/wallet@0.2.2` types)
|
||
|
|
- `getXpub(path): string` — reads pre-cached xpubs from `BuiltInWallet`
|
||
|
|
- `getPublicKey(path, index)` — throws; the shipped SDK never calls it
|
||
|
|
- `signTransaction(request): Promise<{ signedTransaction: string }>` —
|
||
|
|
unwraps the `WcSignTransactionRequest` (the bug Deviant hit and fixed —
|
||
|
|
see the integration guide §4), renders the same signing modal the
|
||
|
|
`window.bcnr` provider uses, and signs via `coSignTransaction`-equivalent
|
||
|
|
logic in `wallet-web.js`
|
||
|
|
- Signing uses **SIGHASH_ALL | FORKID | UTXOS (0x61)**. Normative in the
|
||
|
|
WizardConnect spec; Theseus's signer must accept this flag alongside 0x41.
|
||
|
|
See Deviant `signer.ts:51` `WIZARDCONNECT_SIGHASH` for the reference.
|
||
|
|
|
||
|
|
### 4.2 Pairing UX
|
||
|
|
|
||
|
|
- Address-bar chip when a `wiz://` URI is present on a page (paste-detect).
|
||
|
|
- Settings > Wallet > "Pair with a dApp" — QR scanner (uses the camera if
|
||
|
|
available; otherwise paste), takes a `wiz://` URI, opens the wallet-side
|
||
|
|
connection.
|
||
|
|
- **Sign-request modal is the SAME modal** as the in-Theseus `bcnr` provider.
|
||
|
|
A dApp asking for a signature via `window.bcnr` from a loaded page and a
|
||
|
|
dApp asking via WizardConnect from a paired external site both hit the same
|
||
|
|
confirmation surface. This is the property that makes the responder feel
|
||
|
|
"already part of the browser" rather than a second wallet UX.
|
||
|
|
|
||
|
|
### 4.3 Session persistence
|
||
|
|
|
||
|
|
- Reuse `@wizardconnect/dapp`'s `session` mechanism on the wallet side: the
|
||
|
|
Nostr identity per URI is deterministic, so a reconnect after a Theseus
|
||
|
|
restart is transparent.
|
||
|
|
- Persist active pairings in `<userData>/wallet-wz-sessions.json`.
|
||
|
|
- Settings > Wallet > Paired dApps: list, revoke, "disconnect all".
|
||
|
|
|
||
|
|
### 4.4 The distinction to keep visible in code
|
||
|
|
|
||
|
|
**In-Theseus dApp using `window.bcnr` = local IPC.** No relay, no Nostr, no
|
||
|
|
transport. Fast and private.
|
||
|
|
|
||
|
|
**External dApp using WizardConnect = Nostr relay.** Encrypted end-to-end (NIP-17
|
||
|
|
gift-wrap), but the relay sees timing and message sizes. Users should know they
|
||
|
|
picked the second one when they scanned a QR.
|
||
|
|
|
||
|
|
The wallet doesn't need to distinguish for *signing purposes* — same modal,
|
||
|
|
same signer, same permission model — but the address-bar chip and the settings
|
||
|
|
page should visibly separate `Connected sites` from `Paired dApps`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 5. UI surfaces
|
||
|
|
|
||
|
|
New WebContentsView overlays, each needing a preload extension registered and
|
||
|
|
listed in `TheseusNavigator/package.json` `build.files` (per `GOTCHAS.md`):
|
||
|
|
|
||
|
|
| Overlay | Preload | Purpose |
|
||
|
|
|---|---|---|
|
||
|
|
| `wallet-connect-modal.html` | `wallet-connect-preload.js` | first-use connect prompt |
|
||
|
|
| `wallet-sign-modal.html` | `wallet-sign-preload.js` | signature/payment/name-op confirmation |
|
||
|
|
| `wallet-pair-modal.html` | `wallet-pair-preload.js` | WizardConnect QR scan / URI paste |
|
||
|
|
|
||
|
|
The **address-bar chip** grows an icon when:
|
||
|
|
- The current origin has an active permission (green key)
|
||
|
|
- The current page contains a `wiz://` URI (purple wand)
|
||
|
|
- The wallet is locked and the page is asking for something (grey with a lock)
|
||
|
|
|
||
|
|
Click the chip → shows a summary: which origin, which methods granted, spend
|
||
|
|
so far, revoke options.
|
||
|
|
|
||
|
|
**Settings > Wallet** — a new pane alongside Passwords:
|
||
|
|
- Account row: address, balance, "backup phrase" (behind master-password
|
||
|
|
re-prompt)
|
||
|
|
- Connected sites table
|
||
|
|
- Paired dApps table (WizardConnect)
|
||
|
|
- Transaction history (local; the on-chain source of truth is queryable via
|
||
|
|
the chip-per-tx explorer link)
|
||
|
|
- Auto-lock timeout
|
||
|
|
- **Danger zone**: reset wallet (wipes local state, does not touch on-chain
|
||
|
|
registrations — the certificates stay in the on-chain wallet the seed
|
||
|
|
controls)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 6. Storage & unlock
|
||
|
|
|
||
|
|
- **Wallet key material lives in `<userData>/wallet.enc`**, encrypted the same
|
||
|
|
way passwords are: PBKDF2 → AES-256-GCM under the master password (see
|
||
|
|
[DESIGN-password-manager.md](DESIGN-password-manager.md) for the discipline).
|
||
|
|
- **Same master password** unlocks both the vault and the wallet. Unlocking one
|
||
|
|
unlocks the other for the session. The user picks the password once.
|
||
|
|
- **Distinct purpose subtree** — the seed derives a wallet purpose root via
|
||
|
|
HKDF with `info = "silentmode/wallet/0"`, independent of the password purpose
|
||
|
|
root at `"silentmode/passwords/0"`. Extend `password-vault.js`'s
|
||
|
|
`seedToPurposeRoot(...)` — it already takes a purpose string.
|
||
|
|
- **The signing key is derived on demand** from the purpose root, not held in
|
||
|
|
RAM long-term. Each `signMessage` / `sendPayment` reprises the derivation
|
||
|
|
from the unlocked purpose root.
|
||
|
|
- **Auto-lock** re-encrypts the purpose root under a fresh session key that is
|
||
|
|
discarded on lock. Unlock re-derives from the master password.
|
||
|
|
|
||
|
|
Cross-strand rule: **never derive a wallet key from the password purpose root
|
||
|
|
(or vice versa).** This is the discipline that keeps a website-XSS-driven
|
||
|
|
password leak from also leaking wallet keys. Documented explicitly in
|
||
|
|
`DESIGN-password-manager.md`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 7. Sequencing — the roadmap in order
|
||
|
|
|
||
|
|
| Phase | Roadmap ref | What ships | Depends on |
|
||
|
|
|---|---|---|---|
|
||
|
|
| B.1 | this doc | design doc, user aligned on permission model | — |
|
||
|
|
| B.2a | roadmap B.2 | preload injection scaffold, read-only APIs only | — |
|
||
|
|
| B.2b | roadmap B.2 | eTLD+1 origin binding (share PSL snapshot with A.2) | — |
|
||
|
|
| B.3a | roadmap B.3 | wallet unlock flow reused from password vault | A.2 shipped |
|
||
|
|
| B.3b | roadmap B.3 | `signMessage` + connect modal + settings-connected-sites | B.2 + B.3a |
|
||
|
|
| B.3c | roadmap B.3 | `sendPayment` with per-amount cap | B.3b |
|
||
|
|
| B.4 | roadmap B.4 | `registerName` / `updateName` / `transferName` | B.3b |
|
||
|
|
| **W.1** | *(new)* | WizardConnect responder — WalletAdapter + pair modal | B.3b |
|
||
|
|
| **W.2** | *(new)* | Settings-paired-dApps, session persistence, revoke | W.1 |
|
||
|
|
|
||
|
|
WizardConnect is new to the roadmap — the existing `B.6 — Advanced connect-
|
||
|
|
wallet UX` bullet was vague and `B.5 — Nostr messenger` conflates transport
|
||
|
|
with client. Suggest updating the roadmap: **replace `B.6` with `W.1`+`W.2`
|
||
|
|
above**, keep `B.5` for a Nostr messenger client if that's still wanted, but
|
||
|
|
they are independent projects that happen to share a transport.
|
||
|
|
|
||
|
|
W.1 depends on B.3b (the sign modal) so the same modal serves both surfaces.
|
||
|
|
Do NOT ship W.1 before there is a working sign modal, or the WizardConnect
|
||
|
|
responder will ship its own approval UI and the two will drift.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 8. Open questions — decide before writing B.2+ code
|
||
|
|
|
||
|
|
1. **Chain switching.** Chipnet only in v1 — but what does `window.bcnr` do on
|
||
|
|
a mainnet dApp that expects mainnet? Options:
|
||
|
|
(a) Error with a clear message,
|
||
|
|
(b) present a "this is a chipnet wallet" modal,
|
||
|
|
(c) support both networks with a per-origin chain preference.
|
||
|
|
*Recommend (a) for v1; revisit at mainnet switch.*
|
||
|
|
2. **`bcnr.sendPayment` recipient by name.** If `to: "alice.bch"`, do we
|
||
|
|
resolve the `a` record (payment cashaddr) automatically, or force the caller
|
||
|
|
to `bcnr.resolveName` first and pass a cashaddr? *Recommend auto-resolve;
|
||
|
|
show the resolved address in the modal for the user to check.*
|
||
|
|
3. **QR scanner for pairing.** Camera-based (Electron desktop can access
|
||
|
|
camera, but macOS permission is friction) vs paste-only. *Recommend
|
||
|
|
paste-first, camera as an enhancement in W.2 or later.*
|
||
|
|
4. **BCNR-name provenance in the modal.** Show a "registered on-chain since
|
||
|
|
height X, by category Y" line for BCNR-native origins? Helpful for advanced
|
||
|
|
users, noise for others. *Recommend a collapsed disclosure that opens on
|
||
|
|
click, not a default-visible field.*
|
||
|
|
5. **Rate-limiting on read-only methods.** A page loop calling `resolveName`
|
||
|
|
thousands of times could DoS the resolver pool. *Recommend a per-origin
|
||
|
|
token bucket in the preload — 20 requests/second, drops the excess with a
|
||
|
|
normal-looking error.*
|
||
|
|
6. **Backup phrase display.** Behind the master password re-prompt, plain
|
||
|
|
text or blur-until-hover? *Recommend blur-until-hover with a "why is this
|
||
|
|
dangerous" panel that must be dismissed once before the phrase reveals.*
|
||
|
|
7. **Transaction history storage.** Local JSON, encrypted like the vault, or
|
||
|
|
just derived from chain queries on demand? *Recommend derived-on-demand for
|
||
|
|
v1 — no new storage, no sync surface, and the chain is authoritative.*
|
||
|
|
8. **`transferName` — does it need commit-reveal?** No, because a certificate
|
||
|
|
transfer is just a token send; the commit-reveal spec applies to
|
||
|
|
*registrations*. But the modal should still say "this sends the on-chain
|
||
|
|
ownership token to a stranger and cannot be undone."
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 9. What this design does NOT decide
|
||
|
|
|
||
|
|
- The exact modal visual design. Ship a functional MVP; polish in a UX pass.
|
||
|
|
- The wallet's home screen (balance, recent transactions). Settings > Wallet
|
||
|
|
is sufficient in v1; a dedicated pane is a v2 conversation.
|
||
|
|
- On-chain price display (fiat quotes). Deliberate — Silent Mode does not run
|
||
|
|
a fiat oracle, and pulling from an external price API undoes half the point
|
||
|
|
of the project.
|
||
|
|
- Multi-account UX. Single account per profile; sub-accounts / vanity
|
||
|
|
accounts are a future strand.
|
||
|
|
- Whether the wallet ships to Ariadne mobile as a companion. That is a
|
||
|
|
separate mobile project; the design here informs it but does not require it.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 10. References (do not re-derive)
|
||
|
|
|
||
|
|
- Existing wallet:
|
||
|
|
[`Argus/src/lib/wallet-web.js:BuiltInWallet`](../Argus/src/lib/wallet-web.js)
|
||
|
|
- Existing registration flow: [`Argus/src/lib/registrar.js`](../Argus/src/lib/registrar.js),
|
||
|
|
[`Argus/src/lib/register-tx.js`](../Argus/src/lib/register-tx.js)
|
||
|
|
- Existing dApp-side WizardConnect (SilentMode registrar): [`Argus/src/lib/connect/wizardconnect.js`](../Argus/src/lib/connect/wizardconnect.js)
|
||
|
|
- Existing wallet-side WizardConnect (Deviant, chipnet-validated):
|
||
|
|
[`../../Deviant/Parameters/WalletConnect/WIZARDCONNECT-INTEGRATION-GUIDE.md`](../../Deviant/Parameters/WalletConnect/WIZARDCONNECT-INTEGRATION-GUIDE.md)
|
||
|
|
- WizardConnect LGPL analysis: [`../Decentralized.DNS/WIZARDCONNECT-LICENSE-FINDING.md`](../Decentralized.DNS/WIZARDCONNECT-LICENSE-FINDING.md)
|
||
|
|
- Crypto discipline (purpose subtrees, PBKDF2, HKDF):
|
||
|
|
[`DESIGN-password-manager.md`](DESIGN-password-manager.md)
|
||
|
|
- Registration flow status: [`../Decentralized.DNS/REGISTRATION-STATUS.md`](../Decentralized.DNS/REGISTRATION-STATUS.md)
|