@ducat-unit/ducat-snap 0.1.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/AUDIT_SCOPE.md ADDED
@@ -0,0 +1,46 @@
1
+ # Ducat Snap Audit Scope
2
+
3
+ This file defines the minimum security review scope before publishing `@ducat-unit/ducat-snap` or submitting it for MetaMask allowlisting.
4
+
5
+ ## In Scope
6
+
7
+ - `snap.manifest.json` permissions and allowed origins.
8
+ - BIP32 entropy use for `m/84'/1'` and `m/86'/1'`.
9
+ - Local BIP32 child derivation in `src/bip32.ts`.
10
+ - Account derivation in `src/accounts.ts`.
11
+ - BIP322-style message signing in `src/message.ts`.
12
+ - PSBT parsing, validation, summarization, and signing in `src/psbt.ts`.
13
+ - Taproot script-path commitment verification in `src/psbt.ts`.
14
+ - Ducat vault sequence and OP_RETURN return-data decoding in `src/psbt.ts`.
15
+ - Transfer construction, signing, and broadcast in `src/transfer.ts`.
16
+ - RPC routing and origin authorization in `src/rpc.ts`.
17
+ - User confirmation content in `src/confirmations.ts`.
18
+ - State storage in `src/state.ts`.
19
+ - Home page network fetches in `src/home.ts`.
20
+ - Build artifacts produced by `npm run verify`.
21
+
22
+ ## Required Findings To Rule Out
23
+
24
+ - Any path that exports, logs, stores, or returns private keys or raw entropy.
25
+ - Any signing path that signs an input not explicitly listed in `signInputs`.
26
+ - Any signing path that signs inputs for addresses not derived by the Snap.
27
+ - Any irreversible operation that can proceed without a MetaMask confirmation.
28
+ - Any mainnet key path, address, broadcast endpoint, or signing support in V1.
29
+ - Any unauthorized origin able to invoke Snap RPC methods.
30
+ - Any malformed PSBT, wrong-network PSBT, unknown address, or unknown input index that is accepted.
31
+ - Any package dependency that violates MetaMask Snap SES constraints or creates avoidable key-management risk.
32
+ - Any Taproot script-path behavior that signs a Ducat vault input without proving the tapleaf commits to the prevout output key and contains the derived vault pubkey.
33
+
34
+ ## Audit Evidence
35
+
36
+ The final audit package should include:
37
+
38
+ - Public source repository URL.
39
+ - Audited commit hash.
40
+ - Fixed commit hash, if fixes are required.
41
+ - `npm pack --dry-run` output.
42
+ - `npm audit --omit=dev` output.
43
+ - `npm run verify` output.
44
+ - Snapper/security scan output.
45
+ - Final `snap.manifest.json` shasum.
46
+ - Demo video URL following `DEMO_SCRIPT.md`.
package/DEMO_SCRIPT.md ADDED
@@ -0,0 +1,35 @@
1
+ # Ducat Snap Demo Script
2
+
3
+ Use this script for the MetaMask allowlist/directory submission video. Record against the audited build and the same package version submitted to npm.
4
+
5
+ ## Setup
6
+
7
+ 1. Build and serve the Snap with `npm run verify` and `npm run serve`.
8
+ 2. Configure the Ducat frontend with `NEXT_PUBLIC_DUCAT_SNAP_ID` pointing at the local Snap during pre-submission testing, or `npm:@ducat-unit/ducat-snap` for the npm build.
9
+ 3. Use signet or mutinynet only.
10
+ 4. Fund the Snap-derived sats account with test BTC.
11
+
12
+ ## Recording Steps
13
+
14
+ 1. Open the Ducat frontend and choose MetaMask from the wallet list.
15
+ 2. Show the MetaMask Snap install request and permissions.
16
+ 3. Approve the Snap installation.
17
+ 4. Connect and show the derived `sats`, `runes`, and `vault` addresses in the Ducat app.
18
+ 5. Open MetaMask Snap home and show account addresses, BTC balance, UNIT balance, vault status, recent actions, clickable HTTPS app links, and copyable local routes.
19
+ 6. Execute or stage a create-vault flow and show the compact Ducat PSBT confirmation summary, inputs, outputs, fees, warnings, and app metadata.
20
+ 7. Execute or stage deposit BTC and show the confirmation summary.
21
+ 8. Execute or stage borrow UNIT and show the confirmation summary.
22
+ 9. Execute or stage repay UNIT and show the confirmation summary.
23
+ 10. Execute or stage withdraw BTC and show the confirmation summary.
24
+ 11. Execute or stage UNIT swap and show the confirmation summary.
25
+ 12. Execute or stage liquidation/repossess and show the batch confirmation.
26
+ 13. Sign an authentication message and show the message confirmation.
27
+ 14. Disable and re-enable the Snap, then reconnect from the Ducat frontend.
28
+
29
+ ## Evidence To Capture
30
+
31
+ - Every irreversible signing action shows a MetaMask confirmation.
32
+ - Confirmations include origin, network, signed input indexes, output summary, and fee when calculable.
33
+ - Private keys are never exported or shown.
34
+ - Mainnet is not available in the V1 manifest or UI.
35
+ - Xverse and UniSat remain available in the wallet list.
@@ -0,0 +1,36 @@
1
+ # Dependency Audit Notes
2
+
3
+ Last local production audit:
4
+
5
+ ```bash
6
+ npm run audit:prod
7
+ ```
8
+
9
+ Result: 0 production vulnerabilities.
10
+
11
+ Last local full audit:
12
+
13
+ ```bash
14
+ npm audit
15
+ ```
16
+
17
+ Result: 31 development-toolchain vulnerabilities, all low or moderate.
18
+
19
+ ## Current Assessment
20
+
21
+ The published Snap package contains the production bundle and release metadata. `npm audit --omit=dev` is clean for production dependencies.
22
+
23
+ Direct `dependencies` and `devDependencies` in `package.json` are pinned to exact versions. Transitive dependency versions are locked by `package-lock.json`.
24
+
25
+ The full audit findings are in development tooling paths, primarily transitive dependencies of MetaMask Snap build/test tooling. `npm audit fix --package-lock-only` did not resolve them. npm reports that the remaining forced path would install older breaking versions of MetaMask Snap packages, so it was not applied locally.
26
+
27
+ ## Release Requirement
28
+
29
+ Before public submission, re-run:
30
+
31
+ ```bash
32
+ npm audit
33
+ npm run audit:prod
34
+ ```
35
+
36
+ Then review whether newer compatible MetaMask Snap SDK/CLI releases are available, or document auditor approval for the remaining dev-toolchain findings if production dependencies remain clean.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ducat
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/LISTING.md ADDED
@@ -0,0 +1,44 @@
1
+ # Ducat Snap Listing
2
+
3
+ ## Short Description
4
+
5
+ Ducat Bitcoin accounts and Ducat-aware transaction signing in MetaMask.
6
+
7
+ ## Long Description
8
+
9
+ Ducat is a MetaMask Snap for using Ducat with Bitcoin signet and mutinynet accounts derived inside MetaMask. The Snap derives deterministic testnet Bitcoin accounts from the user's MetaMask Secret Recovery Phrase, keeps private keys inside MetaMask, and signs only explicit PSBT inputs requested by the Ducat web app.
10
+
11
+ The Ducat web app remains the action surface for create, deposit, borrow, repay, withdraw, swap, liquidation, and repossess flows. The Snap provides account discovery, BIP322-style message signing, PSBT signing, batch PSBT signing, a simple transfer path, Ducat-aware MetaMask confirmations, pending/completed/failed action notifications, recent action history, and a Snap home page with copyable account addresses, BTC/UNIT balance, vault summary lookups, recent activity, and Ducat app routes.
12
+
13
+ Mainnet is intentionally not enabled in this release.
14
+
15
+ ## Permissions Rationale
16
+
17
+ - `endowment:rpc`: Allows only approved Ducat frontend origins to invoke the Snap.
18
+ - `snap_getBip32Entropy`: Derives Bitcoin testnet account keys for `m/84'/1'` and `m/86'/1'`.
19
+ - `snap_dialog`: Shows mandatory confirmations before message signing, PSBT signing, batch signing, or transfers.
20
+ - `snap_manageState`: Stores recent Ducat action metadata for Snap home.
21
+ - `snap_notify`: Shows informational MetaMask notifications for pending approvals, completed actions, and failed non-rejection actions.
22
+ - `endowment:page-home`: Shows Ducat account status and Ducat app routes in MetaMask.
23
+ - `endowment:network-access`: Fetches public balance, vault, fee, UTXO, and broadcast data.
24
+ - `endowment:lifecycle-hooks`: Shows the install notice.
25
+
26
+ ## Screenshot Checklist
27
+
28
+ - Install approval screen for the Ducat Snap.
29
+ - Ducat app wallet modal showing MetaMask as a connector.
30
+ - Connected Ducat account addresses.
31
+ - PSBT confirmation for create or deposit showing an action title, origin, testnet network, compact summary rows, signed input details, output details, fees, warnings, and Ducat app metadata.
32
+ - Batch confirmation for liquidation or repossess showing all-or-nothing signing, per-transaction rows, total fee, and warnings.
33
+ - Message signing confirmation showing origin, network, signing account, BIP322 signature type, message fingerprint, message length, and copyable message body.
34
+ - Transfer confirmation showing amount, fee, the `You pay` amount, change, sender, recipient, selected UTXOs, and broadcast endpoint.
35
+ - Snap home showing structured cards for accounts, BTC balance, UNIT balance, vault status, recent actions, clickable HTTPS app links, and copyable local development routes.
36
+
37
+ ## Listing Asset Checklist
38
+
39
+ - Square icon: `images/icon.svg`.
40
+ - Screenshots captured from the audited build.
41
+ - Demo video following `DEMO_SCRIPT.md`.
42
+ - Privacy policy from `PRIVACY.md` or approved legal URL.
43
+ - Support and escalation contact from `SUPPORT.md`.
44
+ - Security disclosure process from `SECURITY.md`.
package/PRIVACY.md ADDED
@@ -0,0 +1,13 @@
1
+ # Privacy Policy
2
+
3
+ Last updated: 2026-06-12
4
+
5
+ The Ducat Snap derives Bitcoin accounts locally inside MetaMask using the user's MetaMask Secret Recovery Phrase. Private keys never leave MetaMask.
6
+
7
+ The Snap stores only recent Ducat action metadata needed for the Snap home page, such as action type, action title, status, network, origin, timestamp, approximate amount, summary text, and transaction IDs. The Snap also remembers the last connected Ducat app origin and network so Snap Home can show the correct testnet and Ducat app routes.
8
+
9
+ The Snap may query public Bitcoin indexer APIs and Ducat validator APIs to display balances, display vault status, estimate fees, fetch UTXOs, and broadcast transactions. Those services may receive public Bitcoin addresses, transaction identifiers, requested network, and normal request metadata such as IP address and user agent.
10
+
11
+ The Snap does not collect analytics, sell user data, or expose private keys. The Snap does not store seed phrases, private keys, or signed transaction secrets in Snap state.
12
+
13
+ Users can remove locally stored recent-action metadata by invoking the confirmed `ducat_clearRecentActions` RPC method from an approved Ducat frontend origin, or by removing the Snap from MetaMask.
package/README.md ADDED
@@ -0,0 +1,234 @@
1
+ # Ducat MetaMask Snap
2
+
3
+ `@ducat-unit/ducat-snap` is the Ducat Bitcoin account and signing Snap for MetaMask.
4
+
5
+ The Snap derives deterministic Bitcoin testnet accounts from the user's MetaMask Secret Recovery Phrase and exposes a small Ducat JSON-RPC API to the Ducat frontend. The Ducat web app remains the action surface. Users create, deposit, borrow, repay, withdraw, swap, and liquidate in the web app; the Snap handles account derivation, MetaMask confirmations, PSBT/message signing, transfer signing, recent action state, action notifications, and a Snap home page.
6
+
7
+ ## Launch Scope
8
+
9
+ - Proposed Snap name: `Ducat`
10
+ - npm package: `@ducat-unit/ducat-snap`
11
+ - Snap ID after publish: `npm:@ducat-unit/ducat-snap`
12
+ - Local development Snap ID: `local:http://localhost:8080`
13
+ - V1 networks: `signet` and `mutinynet`
14
+ - Mainnet: intentionally disabled until audit, soak testing, and allowlist approval
15
+ - Derivation paths:
16
+ - sats: `m/84'/1'/0'/0/0`, P2WPKH `tb1q...`, compressed 33-byte public key
17
+ - runes/vault: `m/86'/1'/0'/0/0`, P2TR `tb1p...`, x-only 32-byte internal public key
18
+
19
+ ## Requirements
20
+
21
+ - Node.js and npm. The repo is currently verified with the bundled Codex Node runtime, Node `24.14.0`.
22
+ - MetaMask with Snaps support.
23
+ - For `local:http://localhost:8080`, MetaMask must allow local Snap fetching. If MetaMask shows `Fetching local snaps is disabled`, enable local Snap development in MetaMask/Flask developer settings, then retry the install/update.
24
+ - A Ducat frontend checkout configured to use this Snap. The current integration branch uses `/Users/lucasrodriguez/Desktop/Ducat/frontend-metamask-snap`.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ cd /Users/lucasrodriguez/Desktop/Ducat/SNAP
30
+ npm ci
31
+ ```
32
+
33
+ ## Verify
34
+
35
+ Use the normal development gate while editing:
36
+
37
+ ```bash
38
+ npm run type-check
39
+ npm test
40
+ npm run build
41
+ npm run manifest
42
+ ```
43
+
44
+ Or run the combined gate:
45
+
46
+ ```bash
47
+ npm run verify
48
+ ```
49
+
50
+ The stricter release gate also runs the production dependency audit, Snapper, and an npm package dry-run:
51
+
52
+ ```bash
53
+ npm run verify:release
54
+ ```
55
+
56
+ ## Run Locally
57
+
58
+ Serve the Snap manifest and bundle:
59
+
60
+ ```bash
61
+ cd /Users/lucasrodriguez/Desktop/Ducat/SNAP
62
+ npm run serve
63
+ ```
64
+
65
+ The Snap is served from:
66
+
67
+ ```text
68
+ http://localhost:8080
69
+ ```
70
+
71
+ Every source, dependency, or icon change can change `snap.manifest.json`'s `source.shasum`. After changing the Snap, run:
72
+
73
+ ```bash
74
+ npm run build
75
+ npm run manifest
76
+ ```
77
+
78
+ Then restart `npm run serve` if the server is already running.
79
+
80
+ ## Frontend Setup
81
+
82
+ In the Ducat frontend `.env.local`, point the MetaMask connector at the local Snap:
83
+
84
+ ```bash
85
+ NEXT_PUBLIC_DUCAT_SNAP_ID="local:http://localhost:8080"
86
+ NEXT_PUBLIC_DUCAT_SNAP_VERSION=""
87
+ ```
88
+
89
+ For the published Snap, use:
90
+
91
+ ```bash
92
+ NEXT_PUBLIC_DUCAT_SNAP_ID="npm:@ducat-unit/ducat-snap"
93
+ NEXT_PUBLIC_DUCAT_SNAP_VERSION="^0.1.0"
94
+ ```
95
+
96
+ Run the frontend on an origin allowed by `snap.manifest.json`, for example:
97
+
98
+ ```bash
99
+ cd /Users/lucasrodriguez/Desktop/Ducat/frontend-metamask-snap
100
+ npm run dev -- -p 3002
101
+ ```
102
+
103
+ Allowed local origins are:
104
+
105
+ - `http://localhost:3000`
106
+ - `http://localhost:3001`
107
+ - `http://localhost:3002`
108
+ - `http://localhost:3003`
109
+
110
+ Allowed HTTPS QA origins are:
111
+
112
+ - `https://dev-git-feat-metamask-snap-connector-ducat.vercel.app`
113
+
114
+ These localhost origins are intentionally present for local development and Snap QA only. Do not remove them from the development manifest during local testing.
115
+
116
+ ### Known Deferred Issue: Localhost Manifest Origins
117
+
118
+ `snap.manifest.json` currently includes localhost origins. That is intentional for local Snap development and QA, but the current development manifest is not directory-ready. Do not remove the localhost origins in this iteration; handle this as a dedicated release-manifest task before MetaMask submission.
119
+
120
+ Release update plan:
121
+
122
+ 1. Keep the current development manifest available for local Snap testing.
123
+ 2. Add a release manifest path or manifest-generation mode that removes every localhost origin.
124
+ 3. Keep only approved HTTPS Ducat frontend origins in `endowment:rpc.allowedOrigins`.
125
+ 4. Rebuild from the audited commit and regenerate the Snap manifest source shasum.
126
+ 5. Update all release artifacts from that exact build: `RELEASE_EVIDENCE.md`, `AUDITOR_HANDOFF.md`, `submission/metamask-directory.json`, `submission/ALLOWLIST_SUBMISSION.md`, package shasum/integrity, screenshots, demo notes, and frontend production Snap env values.
127
+ 6. Run `npm run build`, `npm run manifest`, `npm run verify:release-manifest`, `npm run verify:release`, and `npm run verify:submission-ready`.
128
+ 7. Retag the final audit or submission candidate only after the release manifest, package evidence, and submission metadata are synced.
129
+
130
+ ## MetaMask Local Update Flow
131
+
132
+ 1. Run `npm run serve` in this repo.
133
+ 2. Run the frontend with `NEXT_PUBLIC_DUCAT_SNAP_ID="local:http://localhost:8080"`.
134
+ 3. Connect MetaMask from the Ducat wallet picker.
135
+ 4. If the Snap was already installed and the local shasum changed, use the frontend `Update Snap` button in the wallet picker or connected wallet dropdown.
136
+ 5. Approve the MetaMask update prompt.
137
+ 6. Retry the Ducat action.
138
+
139
+ The connector intentionally does not call `wallet_requestSnaps` before every signature. Signing methods use `wallet_invokeSnap` against the installed Snap, so a stale local build must be updated explicitly.
140
+
141
+ ## JSON-RPC API
142
+
143
+ - `ducat_getAccounts({ network })`
144
+ - `ducat_clearRecentActions()`
145
+ - `ducat_getCapabilities()`
146
+ - `ducat_signMessage({ network, address, message })`
147
+ - `ducat_signPsbt({ network, psbt, signInputs, context })`
148
+ - `ducat_signBatch({ network, entries, context })`
149
+ - `ducat_sendTransfer({ network, address, amountSats, feeRate })`
150
+ - `ducat_getHomeState({ network })`
151
+
152
+ ## What Users See
153
+
154
+ MetaMask confirmations are intentionally action-specific and use structured sections and rows:
155
+
156
+ - Message signing shows the Ducat action label, origin, testnet network, signing account, BIP322 signature type, message length, message fingerprint, and copyable message body.
157
+ - PSBT signing shows the Ducat action label, origin, testnet network, compact summary rows, signed input details, output details, fee, warnings, and Ducat app metadata.
158
+ - Batch signing shows transaction count, all-or-nothing semantics, total fee, per-transaction summaries, and warning count.
159
+ - Simple BTC transfer shows amount, estimated fee, the `You pay` amount, change, sender, recipient, selected UTXO count/value, and broadcast endpoint.
160
+ - Snap Home shows structured cards for the last connected network, copyable BTC/UNIT/vault addresses, BTC and UNIT balances when services are available, vault status, recent action status, clickable links for HTTPS Ducat app origins, and copyable local routes during development. Approved Ducat origins can request a confirmed recent-action history clear.
161
+ - MetaMask notifications announce pending approvals, completed signing/broadcast actions, and non-rejection failures. Notifications are informational only and never gate signing behavior.
162
+
163
+ Errors returned to the frontend are friendly by default and include a stable `code` plus diagnostic `details` for developers. The frontend should display the `message` and keep details available for expanded debugging.
164
+
165
+ ## Troubleshooting
166
+
167
+ | Symptom | Likely Cause | Fix |
168
+ | --- | --- | --- |
169
+ | `Fetching local snaps is disabled` | MetaMask local Snap development is disabled. | Enable local Snap fetching in MetaMask/Flask developer settings, then retry install/update. |
170
+ | MetaMask asks to reinstall before signing | The served local Snap shasum changed. | Run `npm run build && npm run manifest`, restart `npm run serve`, then use the frontend `Update Snap` button. |
171
+ | `This site is not authorized to use the Ducat Snap` | The frontend origin is not in `snap.manifest.json`. | Use one of the allowed local origins or add the origin intentionally and regenerate the manifest. |
172
+ | `This transaction is trying to spend an input from a different Ducat Snap account` | The PSBT input does not match the derived Snap account requested in `signInputs`. | Refresh the frontend wallet account state and rebuild the Ducat transaction. Use diagnostic details to compare expected and actual input addresses. |
173
+ | Balance or vault status is unavailable on Snap Home | Public indexer or Ducat validator lookup timed out or failed. | Retry later. Signing still uses PSBT data supplied by the Ducat app. |
174
+
175
+ ## Security Model
176
+
177
+ - Private keys never leave the Snap.
178
+ - The Snap only requests testnet BIP32 entropy paths in v1.
179
+ - Mainnet requests are rejected.
180
+ - Only explicit `signInputs` indexes are signed.
181
+ - Signing is restricted to derived Ducat Snap accounts.
182
+ - Every message, PSBT, batch, and transfer signing path requires MetaMask confirmation.
183
+ - Friendly frontend action context is display metadata only. Parsed PSBT data is the trusted signing summary.
184
+ - Unauthorized origins cannot invoke the Snap RPC API.
185
+
186
+ ### Taproot Script-Path Policy
187
+
188
+ Vault PSBTs that spend Taproot script-path inputs must include tapleaf and control-block data that recomputes to the prevout P2TR output key. The Snap rejects uncommitted script-path inputs even if the leaf contains the derived Ducat vault key.
189
+
190
+ Mainnet support still requires a separate audit pass, but the signet/mutinynet Snap no longer contains the earlier alpha fallback that accepted uncommitted tapleaf data.
191
+
192
+ ## Audit Readiness Plan
193
+
194
+ Use this plan to move the current audit candidate from "ready to review" to "ready to submit". Do not change `snap.manifest.json` localhost origins as part of this note; handle that as a dedicated release-manifest task before external submission.
195
+
196
+ Current candidate progress: duplicate previous-output rejection, missing previous-output data rejection, oversized signing request rejection, PSBT input/output size guard coverage, suspicious data-output warnings, malformed transfer broadcast txid rejection, bounded primitive app-context metadata validation, hostile app-context containment for decoded vault data, create/borrow/repay/withdraw/repo/liquidate vault action decode coverage, malformed Snap Home balance and vault-data rejection, public-account fixture replay for captured PSBT confirmation text, a HTTPS-only release-manifest verifier, and a final submission-readiness gate for fixtures, E2E evidence, screenshots, audit/demo URLs, and npm metadata are implemented. Remaining external evidence work is real transaction fixture capture, full E2E recording, final support/privacy/escalation details, npm publish evidence, and the third-party audit report.
197
+
198
+ 1. Expand the Ducat transaction fixture corpus. Capture real client-sdk/validator PSBT fixtures for create, deposit, borrow, repay, withdraw, swap, liquidation, and repossess flows on mutinynet/signet. Include the exact Snap `WalletAccountRecord` and expected confirmation text so `npm run verify:fixtures` can replay OP_RETURN decoding, action labels, vault state summaries, data-output labels, warning behavior, and confirmation rendering without private keys.
199
+ 2. Harden PSBT policy tests. Add adversarial cases for wrong network, unknown sign indexes, duplicate inputs, mixed account ownership, malicious frontend context, malformed data outputs, uncommitted Taproot script-path inputs, missing previous-output data, and suspicious zero-value outputs.
200
+ 3. Split development and release configuration. Keep localhost origins and `local:http://localhost:8080` for development, but create a release manifest path that allows only approved HTTPS Ducat origins. Update CI to verify the release manifest, package shasum, and submission metadata together.
201
+ 4. Complete E2E evidence. Record install, update, connect, reload reconnect, create, deposit, borrow, repay, withdraw, swap, liquidation, repossess, rejection, and disabled/re-enabled Snap flows using the same candidate bundle submitted to audit.
202
+ 5. Lock the external submission packet. Finalize privacy/support/escalation contacts, screenshots, demo video, npm publish evidence, audited commit, fixed commit if needed, and the audit report. Update `submission/ALLOWLIST_SUBMISSION.md`, `submission/metamask-directory.json`, and `RELEASE_EVIDENCE.md` from that final candidate.
203
+
204
+ ## Release Path
205
+
206
+ 1. Confirm `npm run verify:release` passes from a clean checkout.
207
+ 2. Update `RELEASE_EVIDENCE.md` for the audited candidate.
208
+ 3. Tag the audit candidate.
209
+ 4. Complete the third-party audit required for `snap_getBip32Entropy`.
210
+ 5. Merge any audit fixes and tag the fixed candidate.
211
+ 6. Publish `@ducat-unit/ducat-snap@0.1.0` to npm.
212
+ 7. Replace pending external fields in `submission/metamask-directory.json` and `submission/ALLOWLIST_SUBMISSION.md`.
213
+ 8. Capture real PSBT fixtures into `submission/fixtures/`.
214
+ 9. Capture final E2E evidence into `submission/e2e/evidence.json`.
215
+ 10. Capture final screenshots into `submission/screenshots/`.
216
+ 11. Record the demo video using `DEMO_SCRIPT.md`.
217
+ 12. Run `npm run verify:submission-ready`.
218
+ 13. Submit the MetaMask allowlist/directory request with audit report, npm URL, public repo URL, demo video, support details, and listing assets.
219
+
220
+ ## Useful Commands
221
+
222
+ ```bash
223
+ npm run type-check
224
+ npm test
225
+ npm run build
226
+ npm run manifest
227
+ npm run serve
228
+ npm run verify
229
+ npm run verify:release
230
+ npm run verify:metadata
231
+ npm run verify:submission-ready
232
+ npm run audit:prod
233
+ npm run pack:dry-run
234
+ ```
@@ -0,0 +1,51 @@
1
+ # Ducat Snap Release Checklist
2
+
3
+ ## Pre-Submission
4
+
5
+ - [x] Public repository contains the current audit-candidate source commit.
6
+ - [x] `.github/workflows/verify.yml` succeeds on the current audit candidate.
7
+ - [x] `npm run verify:release` succeeds locally.
8
+ - [ ] `submission/metamask-directory.json` pending external fields are replaced.
9
+ - [ ] `npm run verify:submission-ready` succeeds after external evidence is complete.
10
+ - [x] `npm run pack:dry-run` output reviewed.
11
+ - [x] `npm run audit:prod` output reviewed.
12
+ - [x] Dependency audit reviewed.
13
+ - [x] Snapper/security scan reviewed.
14
+ - [x] Console logs, unused permissions, accidental placeholders, and dead RPC methods removed or documented as external gates.
15
+ - [x] Alpha Taproot script-path compatibility fallback in `src/psbt.ts` was removed and replaced with committed tapleaf verification.
16
+ - [ ] Real create/deposit/borrow/repay/withdraw/swap/liquidation/repossess PSBT fixtures captured in `submission/fixtures/`.
17
+ - [ ] Final signet/mutinynet E2E evidence captured in `submission/e2e/evidence.json`.
18
+ - [ ] Third-party audit completed because the Snap uses `snap_getBip32Entropy`.
19
+ - [ ] Audit fixes merged and tagged.
20
+
21
+ ## Listing Assets
22
+
23
+ - [x] Icon.
24
+ - [ ] `submission/screenshots/` contains final screenshots.
25
+ - [ ] Screenshots captured from the audited build using `LISTING.md`.
26
+ - [x] Short description from `LISTING.md`.
27
+ - [x] Long description from `LISTING.md`.
28
+ - [x] Privacy policy.
29
+ - [ ] Support and escalation contact.
30
+ - [ ] Demo video recorded with `DEMO_SCRIPT.md`, showing install, connect, create/deposit/borrow/repay/withdraw/swap/liquidation signing, and Snap home.
31
+
32
+ ## Publish
33
+
34
+ - [ ] Publish `@ducat-unit/ducat-snap` to npm.
35
+ - [ ] Submit MetaMask allowlist/directory request with:
36
+ - [ ] audited commit
37
+ - [ ] fixed commit
38
+ - [ ] audit report
39
+ - [ ] npm URL
40
+ - [ ] public repo URL
41
+ - [ ] demo video
42
+ - [ ] support details
43
+ - [ ] listing assets
44
+
45
+ ## Production Cutover
46
+
47
+ - [ ] MetaMask allowlist approval received.
48
+ - [ ] Frontend production config uses `NEXT_PUBLIC_DUCAT_SNAP_ID=npm:@ducat-unit/ducat-snap`.
49
+ - [ ] Frontend production config uses the approved release range in `NEXT_PUBLIC_DUCAT_SNAP_VERSION`.
50
+ - [ ] Existing Xverse and UniSat regression flows pass.
51
+ - [ ] Signet/mutinynet install, connect, create, deposit, borrow, repay, withdraw, swap, and liquidation/repossess E2E scenarios pass.
package/SECURITY.md ADDED
@@ -0,0 +1,13 @@
1
+ # Security
2
+
3
+ Report security issues privately to the Ducat maintainers before public disclosure.
4
+
5
+ Production release requirements:
6
+
7
+ - Third-party audit for key-management usage of `snap_getBip32Entropy`.
8
+ - MetaMask allowlist/directory review.
9
+ - Dependency audit and Snapper/security scan.
10
+ - No mainnet permissions until the mainnet release audit delta is complete.
11
+
12
+ V1 signs only explicit PSBT input indexes that match the Snap-derived signet/mutinynet addresses.
13
+
@@ -0,0 +1,34 @@
1
+ # Snapper Review
2
+
3
+ Last local command:
4
+
5
+ ```bash
6
+ npx --yes @sayfer_io/snapper --path . --output snapper-report.json
7
+ ```
8
+
9
+ Local result: 184 findings, all risk 1 and all under the `ESLinting` category.
10
+
11
+ ## Addressed Findings
12
+
13
+ The first local scan reported 105 findings. The implementation now fixes the non-style/high-signal findings from that run:
14
+
15
+ - Removed an unused `Buffer` import.
16
+ - Replaced empty catch handling with explicit behavior.
17
+ - Removed duplicate taproot key update catch logic by validating `tapInternalKey` before signing.
18
+ - Removed an unused function parameter.
19
+ - Removed an unused type import introduced during the confirmation cleanup.
20
+ - Replaced `||` fallback logic with `??` where empty strings should remain meaningful.
21
+ - Removed the content-type false positive from the hardcoded-secret detector.
22
+
23
+ ## Remaining Finding Summary
24
+
25
+ - 167 missing JSDoc comments.
26
+ - External API response fields using snake_case, such as Esplora and Ducat validator fields (`funded_txo_sum`, `spent_txo_sum`, `chain_stats`, `mempool_stats`, and vault summary fields).
27
+ - One object literal key using `vault_pubkey`, matching the Ducat validator API.
28
+ - One `hmac(sha512, ...)` style warning in local BIP32 derivation.
29
+
30
+ ## Review Notes
31
+
32
+ The remaining findings should be reviewed by the external Snap auditor, but they are not currently treated as local release blockers because they are style or scanner-policy findings rather than signing, key-export, origin-authorization, confirmation-bypass, or network-scope findings.
33
+
34
+ The generated `snapper-report.json` is intentionally ignored and not included in the npm package because it contains machine-local absolute paths. Keep it with release evidence for the audited commit rather than committing it to the public source repository.
package/SUPPORT.md ADDED
@@ -0,0 +1,9 @@
1
+ # Support
2
+
3
+ Use the Ducat Snap repository issue tracker for development support and bug reports:
4
+
5
+ https://github.com/DUCAT-UNIT/ducat-snap/issues
6
+
7
+ Security issues should not be filed as public issues. Follow `SECURITY.md` for private disclosure.
8
+
9
+ For MetaMask directory submission, the public support URL, escalation contact, and response-time expectations must also be copied into `submission/ALLOWLIST_SUBMISSION.md`.