@ducat-unit/wallet-snap 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,12 +4,15 @@ A MetaMask snap for Bitcoin accounts and signing in the [Ducat](https://ducatpro
4
4
 
5
5
  It derives Bitcoin accounts from the user's MetaMask Secret Recovery Phrase, keeps the keys
6
6
  inside MetaMask, and exposes a small JSON-RPC API to the Ducat web app for message signing,
7
- PSBT signing, and simple transfers. The web app drives everything the user does — borrow,
7
+ complete wallet inventory and PSBT signing. The web app drives everything the user does — borrow,
8
8
  repay, withdraw, and so on — and the snap handles account discovery and the MetaMask
9
9
  confirmation prompts.
10
10
 
11
- > Mainnet, signet, and mutinynet are supported. Local development against regtest needs a
12
- > dev build (see below).
11
+ The production artifact supports exactly `mainnet` and `mutinynet`. The
12
+ role-neutral local development artifact adds `regtest`. The Snap has no
13
+ separate `alpha-mainnet` wallet network: its Mainnet profile currently uses the
14
+ reviewed alpha DUCAT contract while retaining canonical Bitcoin mainnet
15
+ address, transaction, and genesis rules.
13
16
 
14
17
  ## What are snaps?
15
18
 
@@ -22,15 +25,40 @@ permissions. See the [MetaMask Snaps docs](https://docs.metamask.io/snaps/) for
22
25
  ```
23
26
  ducat_getAccounts({ network })
24
27
  ducat_getCapabilities()
25
- ducat_getHomeState({ network })
28
+ ducat_getNetwork()
29
+ ducat_getWalletInventory({ network })
26
30
  ducat_signMessage({ network, address, message })
27
31
  ducat_signPsbt({ network, psbt, signInputs, context })
28
32
  ducat_signBatch({ network, entries, context })
29
- ducat_sendTransfer({ network, address, amountSats, feeRate })
30
- ducat_clearRecentActions()
33
+ ducat_switchNetwork({ network })
31
34
  ```
32
35
 
33
- Only the Ducat app origins below are allowed to call these:
36
+ `ducat_getCapabilities()` reports `derivationScheme: "ducat-snap/v1"`.
37
+ The Snap requests the three final managed role nodes directly: sats at
38
+ `m/84'/<coin>'/0'/0/0`, UNIT at `m/86'/<coin>'/0'/0/0`, and vault at
39
+ `m/86'/<coin>'/0'/2/0`, where `<coin>` is `0` on Mainnet and `1` on
40
+ Mutinynet and Regtest. Ducat reserves role branch `2` for the vault client key;
41
+ standard branch `1` remains available for Bitcoin change addresses. Reinstalling
42
+ MetaMask from the same Secret Recovery Phrase recovers the same managed accounts
43
+ for this scheme. An imported key selected in Snap Home is an explicit override
44
+ and is not derived from this scheme.
45
+
46
+ The Snap owns one explicit deployment selection. New production installations default to
47
+ `mutinynet`. `ducat_getNetwork()` returns that selection; `ducat_switchNetwork()`
48
+ changes it only after a MetaMask-owned confirmation showing the requesting origin,
49
+ the From and To networks, the effective validator and Esplora origins, and the
50
+ signing-context warning. A same-network switch is a confirmation-free no-op.
51
+
52
+ Every deployment-sensitive method must exactly match the selected deployment. A mismatch
53
+ fails before account derivation, endpoint access, notifications, signing prompts, or
54
+ state mutation with `NETWORK_MISMATCH` details containing only `selectedNetwork` and
55
+ `requestedNetwork`. `features.explicitNetworkSelection` advertises this contract.
56
+ Legacy state with `lastNetwork` is migrated once to `selectedNetwork`; recent actions,
57
+ keys, endpoint overrides, and origin metadata for supported networks are preserved.
58
+ State belonging to removed `alpha-mainnet`, Signet, or Testnet4 Snap networks is
59
+ discarded rather than renamed or migrated into Mainnet.
60
+
61
+ The production artifact allows exactly these Ducat app origins:
34
62
 
35
63
  ```
36
64
  https://app.ducatprotocol.com
@@ -38,10 +66,54 @@ https://dev.app.ducatprotocol.com
38
66
  https://staging.app.ducatprotocol.com
39
67
  ```
40
68
 
41
- `localhost` and preview deployments are intentionally not in the published manifest — a local
42
- process or a hijacked preview subdomain should never be able to drive mainnet signing. For
43
- local QA, build with the dev origins/regtest flags (`DUCAT_SNAP_DEV_ORIGINS`,
44
- `DUCAT_SNAP_DEV_REGTEST`); those are stripped from the published build.
69
+ `localhost` and preview deployments are intentionally absent from the published
70
+ manifest. The development artifact replaces this list with exactly
71
+ `http://localhost:3000`, `http://localhost:8075`, `http://frontend:3000`, and
72
+ `http://ducat-admin:8075`; it does not inherit production origins. It contains
73
+ exactly `regtest`, `mainnet`, and `mutinynet`, defaults to `mutinynet`, and
74
+ disables debug and unprompted signing.
75
+ The test-only unprompted method refuses every deployment mapped to Bitcoin
76
+ mainnet before wallet state, endpoints, entropy, prompts, or signing are
77
+ touched. Ordinary signing remains prompted for every admitted deployment.
78
+
79
+ Infra builds the ignored development candidate under `.snap/dev`, gates it
80
+ while no server is running, and serves those frozen bytes only at
81
+ `local:http://localhost:8086`. Frontend and Admin are caller workflows of this
82
+ one local Snap; neither is an artifact-policy class. Production packaging stays
83
+ on the isolated production policy and contains no localhost or removed wallet
84
+ network authority. The string `alpha-mainnet` remains only as the Mainnet
85
+ profile's expected validator response while the alpha DUCAT contract is active.
86
+
87
+ From `ducat-infra`, use `make snap-down`, `make snap-install`, and
88
+ `make snap-check` before `make snap-serve`. Starting the Alpha-configured Admin
89
+ does not build, start, stop, or log the shared Snap. Snap install/update, key
90
+ import, funding, prompting, signing, and broadcast each remain separate
91
+ operator approvals. The complete real-Bitcoin sequence and private-key
92
+ handling rules are in
93
+ [`../../dev/runbooks/DUCAT_ADMIN_ALPHA_MAINNET.md`](../../dev/runbooks/DUCAT_ADMIN_ALPHA_MAINNET.md).
94
+
95
+ ## Network Profiles and Endpoint Overrides
96
+
97
+ The Snap ships bundled network profiles in [`src/network-profiles.json`](src/network-profiles.json).
98
+ Each profile carries a canonical Snap network, the Bitcoin network used for
99
+ address, transaction, transport, and genesis mechanics, and the exact
100
+ `chain_network` value expected from its validator. The Mainnet profile currently
101
+ expects `alpha-mainnet`; that value is contract-stage evidence, not another Snap
102
+ network. Retiring the alpha contract requires changing the profile expectation
103
+ and validator URL, not adding or migrating a wallet network.
104
+
105
+ Users can set validator and Esplora endpoint overrides from Snap Home. Overrides are
106
+ stored in Snap state and apply to complete wallet inventory and signing verification.
107
+ Dapps pass only
108
+ the target network name; they do not pass arbitrary endpoint URLs into signing or
109
+ inventory RPC calls. Remote overrides require HTTPS and are verified against the
110
+ selected Bitcoin network before they are stored. An override changes only the URL;
111
+ it cannot change the profile's expected validator identity. Plain HTTP is limited
112
+ to regtest loopback development.
113
+
114
+ Snap Home uses the same confirmed switch flow as website requests. Endpoint and key
115
+ forms re-read the selected network before changing network-scoped state, so a stale
116
+ Home screen cannot modify a different network.
45
117
 
46
118
  ## Development
47
119
 
@@ -52,6 +124,13 @@ npm run manifest # regenerate the manifest shasum
52
124
  npm run serve # serve the snap at http://localhost:8080
53
125
  ```
54
126
 
127
+ The `ducat-infra` local stack uses `make snap-check` to generate and validate
128
+ `.snap/dev`, then `make snap-serve` to serve the unchanged candidate. Localhost
129
+ origins and the development bundle shasum are written only to that ignored
130
+ runtime; the tracked manifest remains the production HTTPS policy. Development preparation rejects missing, duplicate,
131
+ credential-bearing, path-bearing, query-bearing, fragment-bearing, and malformed
132
+ origin entries instead of extending the production list.
133
+
55
134
  Before opening a PR:
56
135
 
57
136
  ```bash
@@ -65,10 +144,13 @@ or at the published package with `NEXT_PUBLIC_DUCAT_SNAP_ID="npm:@ducat-unit/wal
65
144
 
66
145
  ## Security
67
146
 
68
- Keys, child keys, WIFs, and raw entropy never leave the snap. Only derived Ducat accounts are
69
- signed for, only the explicit `signInputs` indexes are signed, and every signing path requires a
70
- MetaMask confirmation. Frontend-supplied context is treated as untrusted display metadata — the
71
- parsed PSBT is the source of truth for what's actually signed. Taproot vault inputs must carry a
147
+ Keys, child keys, WIFs, and raw entropy never leave the snap. The manifest grants entropy access
148
+ only at the six complete `ducat-snap/v1` role paths. Only derived Ducat accounts are
149
+ signed for (with a Snap Home imported-key override taking precedence when present), only the
150
+ explicit `signInputs` indexes are signed, and every signing path requires a
151
+ MetaMask confirmation in the production artifact. Frontend-supplied context is treated as untrusted display metadata — the
152
+ parsed PSBT plus fresh Snap-owned wallet/prevout evidence is the source of truth for what's actually
153
+ signed. Websites construct and broadcast transactions. Taproot vault inputs must carry a
72
154
  Ducat cosign tapleaf whose control block recomputes to the prevout output key.
73
155
 
74
156
  The third-party security audit (Sayfer) is in [`docs/audit.pdf`](docs/audit.pdf).