@utxopia/sdk 0.1.0-alpha.2 → 0.1.0-alpha.3

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.
Files changed (142) hide show
  1. package/README.md +214 -108
  2. package/{packages/sdk/dist → dist}/client.d.ts +25 -1
  3. package/{packages/sdk/dist → dist}/client.js +36 -1
  4. package/{packages/sdk/dist → dist}/crypto-ed25519.d.ts +14 -0
  5. package/{packages/sdk/dist → dist}/crypto-ed25519.js +14 -0
  6. package/{packages/sdk/dist → dist}/index.d.ts +3 -3
  7. package/{packages/sdk/dist → dist}/index.js +3 -3
  8. package/{packages/sdk/dist → dist}/instructions.d.ts +28 -11
  9. package/{packages/sdk/dist → dist}/instructions.js +45 -15
  10. package/{packages/sdk/dist → dist}/stealth.d.ts +104 -9
  11. package/{packages/sdk/dist → dist}/stealth.js +146 -14
  12. package/{packages/sdk/dist → dist}/taproot.d.ts +39 -2
  13. package/{packages/sdk/dist → dist}/taproot.js +54 -2
  14. package/package.json +86 -63
  15. package/src/announcement-client.ts +457 -0
  16. package/src/auditor-ciphertext.ts +181 -0
  17. package/src/auditor.ts +409 -0
  18. package/src/bitcoin/ika.ts +103 -0
  19. package/src/bitcoin/index.ts +5 -0
  20. package/src/bound-params.ts +322 -0
  21. package/src/chadbuffer.ts +603 -0
  22. package/src/circomlibjs.d.ts +51 -0
  23. package/src/claim-link.ts +53 -0
  24. package/src/client.ts +638 -0
  25. package/src/commitment-tree.ts +736 -0
  26. package/src/config.ts +772 -0
  27. package/src/core/esplora.ts +332 -0
  28. package/src/core/mempool.ts +159 -0
  29. package/src/crypto-babyjub.ts +385 -0
  30. package/src/crypto-ed25519.ts +297 -0
  31. package/src/crypto.ts +199 -0
  32. package/src/event-client.ts +231 -0
  33. package/src/events.ts +384 -0
  34. package/src/explorer.ts +300 -0
  35. package/src/index.ts +902 -0
  36. package/src/instructions.ts +2820 -0
  37. package/src/keys.ts +1228 -0
  38. package/src/logger.ts +41 -0
  39. package/src/magicblock.ts +278 -0
  40. package/src/merkle.ts +197 -0
  41. package/src/note.ts +754 -0
  42. package/src/pda.ts +516 -0
  43. package/src/pool-state.ts +176 -0
  44. package/src/poseidon.ts +175 -0
  45. package/src/prover/index.ts +19 -0
  46. package/src/prover/mobile.ts +303 -0
  47. package/src/prover/web.ts +771 -0
  48. package/src/psbt.ts +333 -0
  49. package/src/selective-disclosure.ts +284 -0
  50. package/src/sender-memo.ts +343 -0
  51. package/src/snarkjs.d.ts +19 -0
  52. package/src/sns-resolver.ts +333 -0
  53. package/src/solana/connection.ts +189 -0
  54. package/src/solana/priority-fee.ts +201 -0
  55. package/src/spend-doc.ts +163 -0
  56. package/src/stealth.ts +1477 -0
  57. package/src/taproot.ts +707 -0
  58. package/src/token-registry.ts +207 -0
  59. package/src/utils/encoding.ts +33 -0
  60. package/src/vk-registry.ts +295 -0
  61. package/LICENSE +0 -21
  62. package/packages/btc-client/src/esplora-client.ts +0 -153
  63. package/packages/btc-client/src/index.ts +0 -3
  64. package/packages/btc-client/src/op-return.ts +0 -93
  65. package/packages/btc-client/src/types.ts +0 -112
  66. package/packages/sdk/README.md +0 -277
  67. /package/{packages/sdk/dist → dist}/announcement-client.d.ts +0 -0
  68. /package/{packages/sdk/dist → dist}/announcement-client.js +0 -0
  69. /package/{packages/sdk/dist → dist}/auditor-ciphertext.d.ts +0 -0
  70. /package/{packages/sdk/dist → dist}/auditor-ciphertext.js +0 -0
  71. /package/{packages/sdk/dist → dist}/auditor.d.ts +0 -0
  72. /package/{packages/sdk/dist → dist}/auditor.js +0 -0
  73. /package/{packages/sdk/dist → dist}/bitcoin/ika.d.ts +0 -0
  74. /package/{packages/sdk/dist → dist}/bitcoin/ika.js +0 -0
  75. /package/{packages/sdk/dist → dist}/bitcoin/index.d.ts +0 -0
  76. /package/{packages/sdk/dist → dist}/bitcoin/index.js +0 -0
  77. /package/{packages/sdk/dist → dist}/bound-params.d.ts +0 -0
  78. /package/{packages/sdk/dist → dist}/bound-params.js +0 -0
  79. /package/{packages/sdk/dist → dist}/chadbuffer.d.ts +0 -0
  80. /package/{packages/sdk/dist → dist}/chadbuffer.js +0 -0
  81. /package/{packages/sdk/dist → dist}/claim-link.d.ts +0 -0
  82. /package/{packages/sdk/dist → dist}/claim-link.js +0 -0
  83. /package/{packages/sdk/dist → dist}/commitment-tree.d.ts +0 -0
  84. /package/{packages/sdk/dist → dist}/commitment-tree.js +0 -0
  85. /package/{packages/sdk/dist → dist}/config.d.ts +0 -0
  86. /package/{packages/sdk/dist → dist}/config.js +0 -0
  87. /package/{packages/sdk/dist → dist}/core/esplora.d.ts +0 -0
  88. /package/{packages/sdk/dist → dist}/core/esplora.js +0 -0
  89. /package/{packages/sdk/dist → dist}/core/mempool.d.ts +0 -0
  90. /package/{packages/sdk/dist → dist}/core/mempool.js +0 -0
  91. /package/{packages/sdk/dist → dist}/crypto-babyjub.d.ts +0 -0
  92. /package/{packages/sdk/dist → dist}/crypto-babyjub.js +0 -0
  93. /package/{packages/sdk/dist → dist}/crypto.d.ts +0 -0
  94. /package/{packages/sdk/dist → dist}/crypto.js +0 -0
  95. /package/{packages/sdk/dist → dist}/event-client.d.ts +0 -0
  96. /package/{packages/sdk/dist → dist}/event-client.js +0 -0
  97. /package/{packages/sdk/dist → dist}/events.d.ts +0 -0
  98. /package/{packages/sdk/dist → dist}/events.js +0 -0
  99. /package/{packages/sdk/dist → dist}/explorer.d.ts +0 -0
  100. /package/{packages/sdk/dist → dist}/explorer.js +0 -0
  101. /package/{packages/sdk/dist → dist}/keys.d.ts +0 -0
  102. /package/{packages/sdk/dist → dist}/keys.js +0 -0
  103. /package/{packages/sdk/dist → dist}/logger.d.ts +0 -0
  104. /package/{packages/sdk/dist → dist}/logger.js +0 -0
  105. /package/{packages/sdk/dist → dist}/magicblock.d.ts +0 -0
  106. /package/{packages/sdk/dist → dist}/magicblock.js +0 -0
  107. /package/{packages/sdk/dist → dist}/merkle.d.ts +0 -0
  108. /package/{packages/sdk/dist → dist}/merkle.js +0 -0
  109. /package/{packages/sdk/dist → dist}/note.d.ts +0 -0
  110. /package/{packages/sdk/dist → dist}/note.js +0 -0
  111. /package/{packages/sdk/dist → dist}/pda.d.ts +0 -0
  112. /package/{packages/sdk/dist → dist}/pda.js +0 -0
  113. /package/{packages/sdk/dist → dist}/pool-state.d.ts +0 -0
  114. /package/{packages/sdk/dist → dist}/pool-state.js +0 -0
  115. /package/{packages/sdk/dist → dist}/poseidon.d.ts +0 -0
  116. /package/{packages/sdk/dist → dist}/poseidon.js +0 -0
  117. /package/{packages/sdk/dist → dist}/prover/index.d.ts +0 -0
  118. /package/{packages/sdk/dist → dist}/prover/index.js +0 -0
  119. /package/{packages/sdk/dist → dist}/prover/mobile.d.ts +0 -0
  120. /package/{packages/sdk/dist → dist}/prover/mobile.js +0 -0
  121. /package/{packages/sdk/dist → dist}/prover/web.d.ts +0 -0
  122. /package/{packages/sdk/dist → dist}/prover/web.js +0 -0
  123. /package/{packages/sdk/dist → dist}/psbt.d.ts +0 -0
  124. /package/{packages/sdk/dist → dist}/psbt.js +0 -0
  125. /package/{packages/sdk/dist → dist}/selective-disclosure.d.ts +0 -0
  126. /package/{packages/sdk/dist → dist}/selective-disclosure.js +0 -0
  127. /package/{packages/sdk/dist → dist}/sender-memo.d.ts +0 -0
  128. /package/{packages/sdk/dist → dist}/sender-memo.js +0 -0
  129. /package/{packages/sdk/dist → dist}/sns-resolver.d.ts +0 -0
  130. /package/{packages/sdk/dist → dist}/sns-resolver.js +0 -0
  131. /package/{packages/sdk/dist → dist}/solana/connection.d.ts +0 -0
  132. /package/{packages/sdk/dist → dist}/solana/connection.js +0 -0
  133. /package/{packages/sdk/dist → dist}/solana/priority-fee.d.ts +0 -0
  134. /package/{packages/sdk/dist → dist}/solana/priority-fee.js +0 -0
  135. /package/{packages/sdk/dist → dist}/spend-doc.d.ts +0 -0
  136. /package/{packages/sdk/dist → dist}/spend-doc.js +0 -0
  137. /package/{packages/sdk/dist → dist}/token-registry.d.ts +0 -0
  138. /package/{packages/sdk/dist → dist}/token-registry.js +0 -0
  139. /package/{packages/sdk/dist → dist}/utils/encoding.d.ts +0 -0
  140. /package/{packages/sdk/dist → dist}/utils/encoding.js +0 -0
  141. /package/{packages/sdk/dist → dist}/vk-registry.d.ts +0 -0
  142. /package/{packages/sdk/dist → dist}/vk-registry.js +0 -0
package/README.md CHANGED
@@ -1,170 +1,276 @@
1
- # UTXOpia SDK
1
+ # @utxopia/sdk
2
2
 
3
- TypeScript client for [UTXOpia](https://utxopia.com) a privacy-preserving Bitcoin-to-Solana
4
- bridge. Deposits arrive as commitments in a Merkle tree, transfers move between commitments with
5
- Groth16 JoinSplit proofs, and an amount only becomes public when someone withdraws.
3
+ TypeScript SDK for interacting with the UTXOpia protocol - a privacy-preserving Bitcoin-to-Solana bridge using Zero-Knowledge Proofs.
6
4
 
7
- > **Alpha.** `0.1.0-alpha.1`. The protocol has not been deployed to mainnet — do not point this at
8
- > funds you cannot lose.
9
- >
10
- > **The export surface is not a contract yet.** Roughly 430 things are exported; about a third are
11
- > exercised by the reference apps, and the rest is protocol surface nobody has needed yet or
12
- > helpers that were never deliberately made public. Being pre-1.0 is what makes that survivable:
13
- > the surface will be narrowed to the workflows below before 1.0, and anything outside them may
14
- > disappear in a minor release. Build against what `docs/API-MAP.md` marks as used; if you need
15
- > something else, open an issue so it gets kept on purpose rather than by accident.
16
-
17
- ## Install
5
+ ## Installation
18
6
 
19
7
  ```bash
20
8
  bun add @utxopia/sdk
9
+ # or
10
+ npm install @utxopia/sdk
21
11
  ```
22
12
 
23
- Installing from git instead? Pin a commit, never a branch. A floating `#main` means two apps that
24
- installed a week apart disagree about the note format, and the failure surfaces as a proof that
25
- will not verify rather than as an install error:
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import {
17
+ deriveKeysFromWallet,
18
+ createStealthDeposit,
19
+ scanAnnouncements,
20
+ resolveSnsName,
21
+ } from '@utxopia/sdk';
26
22
 
27
- ```jsonc
28
- // package.json
29
- "@utxopia/sdk": "github:UTXOpia/utxopia-sdk#<full-commit-sha>"
23
+ // 1. Derive keys from wallet
24
+ const keys = await deriveKeysFromWallet(walletAdapter);
25
+
26
+ // 2. Look up recipient by .utxopia.sol name
27
+ const recipient = await resolveSnsName(connection, 'alice');
28
+
29
+ // 3. Create stealth deposit
30
+ const deposit = await createStealthDeposit(recipient, 100000n);
31
+
32
+ // 4. Scan for incoming deposits
33
+ const notes = await scanAnnouncements(keys, announcements);
30
34
  ```
31
35
 
32
- `snarkjs` and `react` are optional peers — install `snarkjs` only if you generate proofs, `react`
33
- only if you use the hooks.
36
+ ## Core Features
37
+
38
+ ### Key Derivation
39
+
40
+ Derive spending and viewing keys from a Solana wallet signature (RAILGUN-style):
34
41
 
35
- ## What the pieces are
42
+ ```typescript
43
+ import { deriveKeysFromWallet, type UTXOpiaKeys } from '@utxopia/sdk';
44
+
45
+ const keys: UTXOpiaKeys = await deriveKeysFromWallet(walletAdapter);
46
+ // keys.spendingPubKey - for receiving funds
47
+ // keys.viewingPubKey - for scanning deposits
48
+ // keys.spendingPrivKey - for claiming (keep secret!)
49
+ // keys.viewingPrivKey - for scanning (can delegate)
50
+ ```
36
51
 
37
- | Concept | What it means here |
38
- |---|---|
39
- | **Note** | A commitment `Poseidon(npk, token, amount)` in the tree. Owning one means knowing the key behind `npk`. |
40
- | **Nullifier** | `Poseidon(nullifyingKey, leafIndex)`. Published on spend so a note cannot be spent twice; it reveals nothing about which note it was. |
41
- | **Stealth address** | A recipient publishes a meta-address; each sender derives a fresh one-time `npk` from it, so payments to the same person are unlinkable on-chain. |
42
- | **JoinSplit(N,M)** | One proof spends N notes and creates M. Shapes with `N + M ≤ 10` are accepted on-chain, 45 in total. |
43
- | **Viewing key** | Decrypts incoming announcements. Separate from the spending key, so scanning can be delegated. |
52
+ ### Stealth Addresses (EIP-5564/DKSAP Pattern)
44
53
 
45
- ## Quick start
54
+ Create private deposits that only the recipient can detect and claim:
46
55
 
47
56
  ```typescript
48
57
  import {
49
- deriveKeysFromWallet,
50
- resolveSnsName,
51
58
  createStealthDeposit,
52
59
  scanAnnouncements,
53
- } from "@utxopia/sdk";
60
+ prepareClaimInputs,
61
+ } from '@utxopia/sdk';
54
62
 
55
- // Keys come from a wallet signature over a fixed message — nothing to store.
56
- const keys = await deriveKeysFromWallet(walletAdapter);
63
+ // Sender: Create stealth deposit
64
+ const deposit = await createStealthDeposit(recipientMeta, amountSats);
65
+ // deposit.ephemeralPub - publish on-chain
66
+ // deposit.commitment - add to Merkle tree
67
+ // deposit.amountSats - verified BTC amount
57
68
 
58
- // Recipients can be a .utxopia.sol name instead of a raw meta-address.
59
- const recipient = await resolveSnsName(connection, "alice");
69
+ // Recipient: Scan for deposits
70
+ const notes = await scanAnnouncements(keys, onChainAnnouncements);
60
71
 
61
- const deposit = await createStealthDeposit(recipient, 100_000n);
72
+ // Recipient: Prepare claim inputs for ZK proof
73
+ const claimInputs = await prepareClaimInputs(keys, note, merkleProof);
74
+ ```
62
75
 
63
- // Scanning is a local trial-decrypt of every announcement — there is no
64
- // "my transactions" endpoint to ask, by design.
65
- const notes = await scanAnnouncements(keys, announcements);
76
+ ### Note Generation
77
+
78
+ Create and manage shielded notes:
79
+
80
+ ```typescript
81
+ import {
82
+ generateNote,
83
+ deriveNote,
84
+ createClaimLink,
85
+ parseClaimLink,
86
+ } from '@utxopia/sdk';
87
+
88
+ // Generate random note
89
+ const note = generateNote(100000n);
90
+
91
+ // Derive deterministic note from seed
92
+ const note = deriveNote('my-secret-phrase', 0, 100000n);
93
+
94
+ // Create shareable claim link
95
+ const link = createClaimLink(note);
96
+
97
+ // Parse claim link
98
+ const parsed = parseClaimLink(link);
66
99
  ```
67
100
 
68
- `docs/API-MAP.md` lists every export grouped by what you are building — config, keys, PDAs,
69
- notes, stealth, Bitcoin, proving, instructions, chain reads, disclosure — and marks which ones
70
- the reference apps actually use. `packages/sdk/README.md` covers each area in depth;
71
- `packages/sdk/docs/SDK.md` has the full type reference.
101
+ ### Taproot Address Derivation
72
102
 
73
- ## Entry points
103
+ Generate BTC deposit addresses:
74
104
 
75
105
  ```typescript
76
- import { } from "@utxopia/sdk"; // everything below is re-exported here
77
- import { … } from "@utxopia/sdk/prover/web"; // browser proving (snarkjs + WebAssembly)
78
- import { } from "@utxopia/sdk/prover/mobile"; // React Native proving
79
- import { } from "@utxopia/sdk/bitcoin"; // taproot, Ika custody
80
- import { } from "@utxopia/sdk/stealth"; // stealth addresses on their own
81
- import { … } from "@utxopia/sdk/btc-client"; // Esplora client, OP_RETURN encoding
106
+ import { deriveTaprootAddress, verifyTaprootAddress } from '@utxopia/sdk';
107
+
108
+ // Use the configured FROST/Ika custody public key; there is no safe default.
109
+ const custodyInternalKey = getConfiguredCustodyInternalKey();
110
+ const { address } = deriveTaprootAddress(
111
+ commitment,
112
+ 'testnet',
113
+ custodyInternalKey,
114
+ );
115
+
116
+ // Verify address matches commitment
117
+ const isValid = verifyTaprootAddress(address, commitment, custodyInternalKey);
82
118
  ```
83
119
 
84
- The root export is the whole surface. The subpaths exist so a bundle that only needs, say,
85
- Esplora does not pull in the prover.
120
+ ### Merkle Proofs
121
+
122
+ Work with the on-chain commitment tree:
123
+
124
+ ```typescript
125
+ import {
126
+ createMerkleProof,
127
+ proofToNoirFormat,
128
+ TREE_DEPTH,
129
+ } from '@utxopia/sdk';
130
+
131
+ const proof = createMerkleProof(leaves, leafIndex);
132
+ const noirProof = proofToNoirFormat(proof);
133
+ ```
86
134
 
87
- ## Naming
135
+ ## API Reference
88
136
 
89
- The prefix tells you what a call costs, so you can read an import list without opening anything:
137
+ ### Stealth Module
90
138
 
91
- | prefix | meaning |
92
- |---|---|
93
- | `derive*PDA` | pure address math, synchronous |
94
- | `fetch*` | hits the network always async |
95
- | `get*` | reads local or already-fetched state synchronous |
96
- | `parse*` | bytes in, typed object out; no IO |
97
- | `build*InstructionData` / `build*Instruction` | encode instruction data / assemble the full instruction |
98
- | `compute*Sync` | Poseidon hashes. Only the `Sync` form exists — poseidon-lite has nothing to await |
99
- | `encode`/`decode`, `pack`/`unpack`, `serialize`/`deserialize` | symmetric pairs, always both directions |
139
+ | Function | Description |
140
+ |----------|-------------|
141
+ | `createStealthDeposit(recipient, amount)` | Create stealth deposit for recipient |
142
+ | `scanAnnouncements(keys, announcements)` | Scan for deposits using viewing key |
143
+ | `prepareClaimInputs(keys, note, proof)` | Prepare inputs for ZK claim proof |
144
+ | `scanUnifiedNotes(keys, announcements)` | Scan announcement events for owned notes |
145
+ | `resolveSnsName(conn, name)` | Look up .utxopia.sol name to stealth address |
100
146
 
101
- `PDA` is spelled in caps. Seven MagicBlock helpers used to say `Pda` and two RPC calls were named
102
- `get*`; they were renamed before the first publish, while doing so was still free.
147
+ ### Key Derivation Module
103
148
 
104
- ## Proving
149
+ | Function | Description |
150
+ |----------|-------------|
151
+ | `deriveKeysFromWallet(wallet)` | Derive keys from wallet signature |
152
+ | `deriveKeysFromSignature(sig)` | Derive keys from raw signature |
153
+ | `deriveKeysFromSeed(seed)` | Derive keys from seed bytes |
154
+ | `createStealthMetaAddress(keys)` | Create stealth meta-address |
155
+ | `createDelegatedViewKey(keys, perms, expiry)` | Create delegated view key |
105
156
 
106
- Proof generation needs circuit artifacts — a `.wasm` witness generator and a `.zkey` — which are
107
- too large to ship in the package. Point the prover at a host serving them:
157
+ ### Constants
108
158
 
109
159
  ```typescript
110
- import { setCircuitPath, generateJoinSplitProof } from "@utxopia/sdk/prover/web";
160
+ // Program IDs
161
+ UTXOPIA_PROGRAM_ID // Main UTXOpia program (devnet)
162
+ CHADBUFFER_PROGRAM_ID // ChadBuffer for SPV proofs
163
+
164
+ // Merkle Tree
165
+ TREE_DEPTH // 20
166
+ MAX_LEAVES // 2^20
167
+ ZERO_VALUE // Empty leaf value
111
168
 
112
- setCircuitPath("https://circuit.utxopia.com/circuits/v2/groth16");
113
- const proof = await generateJoinSplitProof(inputs);
114
169
  ```
115
170
 
116
- **Pin the artifacts you are willing to prove with.** The `.wasm` is the witness generator: it
117
- receives every private input a JoinSplit has — spending key, nullifying key, note randomness,
118
- amounts, the full Merkle path — in the clear. It arrives over plain `fetch`, where subresource
119
- integrity does not apply, and CDNs serve it `immutable, max-age=31536000`, so a substituted file
120
- survives in browser and edge caches long after the origin is cleaned. Proofs built from a poisoned
121
- generator still verify, so nothing fails and no user notices.
171
+ ## Types
122
172
 
123
- ```typescript
124
- import { setCircuitArtifactDigests } from "@utxopia/sdk/prover/web";
125
- import digests from "./circuit-manifest.json"; // generated by your build, committed
173
+ ### UTXOpiaKeys
126
174
 
127
- setCircuitArtifactDigests(digests.artifacts);
175
+ ```typescript
176
+ interface UTXOpiaKeys {
177
+ spendingPubKey: BabyJubPoint;
178
+ spendingPrivKey: bigint;
179
+ viewingPubKey: Uint8Array;
180
+ viewingPrivKey: Uint8Array;
181
+ nullifyingKey: bigint;
182
+ }
128
183
  ```
129
184
 
130
- Keys are paths under the circuit base (`joinsplit_1x1/joinsplit_1x1_js/joinsplit_1x1.wasm`), so one
131
- manifest covers local dev and the CDN. Once set it fails closed: an artifact with no recorded
132
- digest is refused rather than trusted, so the manifest has to cover every shape your origin serves.
133
- The SDK cannot ship these — they change whenever circuits are rebuilt.
185
+ ### StealthDeposit
134
186
 
135
- ## Layout
187
+ ```typescript
188
+ interface StealthDeposit {
189
+ ephemeralPub: Uint8Array; // 32 bytes (Ed25519)
190
+ amountSats: bigint;
191
+ commitment: Uint8Array; // 32 bytes
192
+ createdAt: number;
193
+ }
194
+ ```
136
195
 
196
+ ### ScannedNote
197
+
198
+ ```typescript
199
+ interface ScannedNote {
200
+ amount: bigint;
201
+ ephemeralPub: Uint8Array;
202
+ stealthPub: BabyJubPoint;
203
+ leafIndex: number;
204
+ commitment: Uint8Array;
205
+ }
137
206
  ```
138
- packages/sdk/ the SDK — keys, notes, stealth, proving, Solana instructions
139
- packages/btc-client/ standalone Esplora client and deposit OP_RETURN encoding
207
+
208
+ ### ConnectionAdapter
209
+
210
+ ```typescript
211
+ interface ConnectionAdapter {
212
+ getAccountInfo: (
213
+ pubkey: { toBytes(): Uint8Array }
214
+ ) => Promise<{ data: Uint8Array } | null>;
215
+ }
140
216
  ```
141
217
 
218
+ ## Security Considerations
219
+
220
+ 1. **Never expose spending private key** - Only needed for claiming
221
+ 2. **Viewing key can be delegated** - For balance monitoring without spend capability
222
+ 3. **Nullifiers prevent double-spending** - Derived from spending key + leaf index
223
+ 4. **Commitments hide amounts** - Poseidon hash of NPK, token, and amount
224
+
142
225
  ## Development
143
226
 
144
227
  ```bash
228
+ # Install dependencies
145
229
  bun install
146
- bun run test # 782 tests
147
- bun run build # tsc
230
+
231
+ # Build
232
+ bun run build
233
+
234
+ # Run tests
235
+ bun test
236
+ ```
237
+
238
+ ## Verify it yourself: rebuild the tree from chain
239
+
240
+ Spending a note needs a Merkle proof, and a Merkle proof needs the whole leaf
241
+ set — which normally comes from our indexer. If the leaves weren't recoverable
242
+ from the chain itself, "you can exit without the operator" would be a promise
243
+ rather than a property.
244
+
245
+ This script rebuilds a pool's entire leaf set from Solana transaction logs and
246
+ checks the result against the root the program is verifying against. It talks to
247
+ a public RPC endpoint and nothing else — no backend, no indexer, no API key:
248
+
249
+ ```bash
250
+ TREE=<commitment-tree-pda> bun run scripts/rebuild-tree-from-chain.ts
148
251
  ```
149
252
 
150
- Tests are pure unit tests — no network, no validator, no proving. They run in about eight seconds,
151
- so there is no reason to skip them.
253
+ ```
254
+ on-chain: 123 leaves, root 2994f7d670d12cd8dcbd89af708ff55f9e877bbb387aac4cf09e688a615650ce
255
+ 134/134 scanned, 123 leaves
256
+ recovered 123/123 leaves from logs
257
+ rebuilt: 123 leaves, root 2994f7d670d12cd8dcbd89af708ff55f9e877bbb387aac4cf09e688a615650ce
152
258
 
153
- ## Security
259
+ MATCH — the leaf set is recoverable from chain alone
260
+ ```
154
261
 
155
- Two things worth knowing before you build on this:
262
+ It exits non-zero on a mismatch or on missing leaves, so it works as a check in
263
+ CI as well as by hand.
156
264
 
157
- - **`createDelegatedViewKey` currently hands over the `nullifyingKey`.** Because
158
- `nullifier = Poseidon(nullifyingKey, leafIndex)` does not depend on note contents and leaf
159
- indices are small public integers, anyone holding it can compute every nullifier that key will
160
- ever produce and link the owner's entire spend history, retroactively and permanently. The
161
- `fromSlot`/`toSlot` and FULL/SCAN/INCOMING_ONLY fields on a delegated key are metadata, not
162
- enforcement. Treat a delegated viewing key as handing over the whole account, not a slice of it.
163
- `extractViewOnlyBundle` correctly omits the key and is the safer thing to reach for.
164
- - Keys derive from a wallet signature, so **any party who can make that wallet sign the derivation
165
- message can reconstruct the shielded identity.** A hardware wallet does not change this.
265
+ | Env | Default | |
266
+ |---|---|---|
267
+ | `RPC` | devnet | Any Solana RPC endpoint |
268
+ | `TREE` | `DEVNET_CONFIG.commitmentTreePda` | Commitment tree PDA to rebuild |
269
+ | `EPOCH_SIG` | | Tree's `INITIALIZE` signature. Optional; set it to skip a dead epoch if the PDA was closed and recreated |
270
+ | `PACE_MS` | `120` | Delay between requests. Raise it if your endpoint throttles you |
166
271
 
167
- Report anything else privately rather than in a public issue.
272
+ Public endpoints rate-limit this scan aggressively the script paces itself and
273
+ backs off, so expect it to take a minute or two rather than to fail.
168
274
 
169
275
  ## License
170
276
 
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { type NetworkConfig, type NetworkId } from "./config";
20
20
  import { type AuthSignatureKeyDerivationOptions, type UTXOpiaKeys, type StealthMetaAddress, type WalletSignerAdapter, type KeySetupResult } from "./keys";
21
- import { type ViewOnlyKeys, type StealthOutputWithKeys, type NonInteractiveDepositResult } from "./stealth";
21
+ import { type ViewOnlyKeys, type StealthOutputWithKeys, type NonInteractiveDepositResult, type TweakDepositResult } from "./stealth";
22
22
  import { type UtxoDescriptor } from "./psbt";
23
23
  import { type DepositOpReturnContext } from "./taproot";
24
24
  export interface UTXOpiaClientConfig {
@@ -137,6 +137,30 @@ export declare class UTXOpiaClient {
137
137
  computeNullifier(note: {
138
138
  leafIndex: number;
139
139
  }): Uint8Array;
140
+ /**
141
+ * Prepare an OP_RETURN-free BTC deposit address (`verify_deposit`, disc 25).
142
+ *
143
+ * The ephemeral key is derived from this wallet's own viewing node and
144
+ * `depositIndex`, never randomly. A deposit address commits to the ephemeral
145
+ * key through its tapleaf and its key path is a NUMS point, so an address whose
146
+ * ephemeral key is lost is one nobody can ever spend. Indexing it makes the
147
+ * viewing key a complete backup: walk `depositIndex` upward and every address
148
+ * comes back.
149
+ *
150
+ * Self-deposit only. The address is recovered by whoever holds the viewing key
151
+ * it was derived from, so generating one *for someone else* would hand them a
152
+ * note they own but coins they could never recover. Pay a third party with a
153
+ * shielded transfer instead.
154
+ *
155
+ * `depositIndex` must be persisted and monotonic. Reusing one re-derives the
156
+ * same address, which is safe on chain but links the two deposits.
157
+ */
158
+ prepareTweakDeposit(opts: {
159
+ depositIndex: number;
160
+ ikaXOnlyPubkey: Uint8Array;
161
+ recipient?: StealthMetaAddress;
162
+ network?: "mainnet" | "testnet" | "regtest";
163
+ }): Promise<TweakDepositResult>;
140
164
  /**
141
165
  * Prepare a BTC deposit: generate stealth deposit address + OP_RETURN.
142
166
  * Returns the deposit result (btcAddress, opReturnPayload) ready for PSBT building.
@@ -22,7 +22,7 @@ import { initConfig, getConfig } from "./config";
22
22
  import { parseMerkleProofResponse } from "./merkle";
23
23
  import { eddsaPoseidonSign } from "./keys";
24
24
  import { setupKeysFromWallet, setupKeysFromSeed, setupKeysFromAuthSignature, recreateStealthAddress, serializeKeysForStorage, deserializeKeysFromStorage, clearUTXOpiaKeys, } from "./keys";
25
- import { scanUnifiedNotesMulti, scanAnnouncementsViewOnlyMulti, computeNullifierHashForNote, computeNullifierBytes, isDepositForViewerHex, createDepositFromConfig, createStealthOutputWithKeys, } from "./stealth";
25
+ import { scanUnifiedNotesMulti, scanAnnouncementsViewOnlyMulti, computeNullifierHashForNote, computeNullifierBytes, isDepositForViewerHex, createDepositFromConfig, createTweakDeposit, depositViewingNode, createStealthOutputWithKeys, } from "./stealth";
26
26
  import { selectUtxos } from "./psbt";
27
27
  import { hexToBytes } from "./crypto";
28
28
  import { EventClient } from "./event-client";
@@ -308,6 +308,41 @@ export class UTXOpiaClient {
308
308
  throw new Error("Not authenticated");
309
309
  }
310
310
  // ─── Phase 2: Deposit + Shield ─────────────────────────────────
311
+ /**
312
+ * Prepare an OP_RETURN-free BTC deposit address (`verify_deposit`, disc 25).
313
+ *
314
+ * The ephemeral key is derived from this wallet's own viewing node and
315
+ * `depositIndex`, never randomly. A deposit address commits to the ephemeral
316
+ * key through its tapleaf and its key path is a NUMS point, so an address whose
317
+ * ephemeral key is lost is one nobody can ever spend. Indexing it makes the
318
+ * viewing key a complete backup: walk `depositIndex` upward and every address
319
+ * comes back.
320
+ *
321
+ * Self-deposit only. The address is recovered by whoever holds the viewing key
322
+ * it was derived from, so generating one *for someone else* would hand them a
323
+ * note they own but coins they could never recover. Pay a third party with a
324
+ * shielded transfer instead.
325
+ *
326
+ * `depositIndex` must be persisted and monotonic. Reusing one re-derives the
327
+ * same address, which is safe on chain but links the two deposits.
328
+ */
329
+ async prepareTweakDeposit(opts) {
330
+ if (!this._keys) {
331
+ throw new Error("No keys (login first)");
332
+ }
333
+ const meta = opts.recipient ?? this._stealthAddress;
334
+ if (!meta)
335
+ throw new Error("No recipient stealth address (login first or provide recipient)");
336
+ if (opts.recipient && this._stealthAddress && opts.recipient !== this._stealthAddress) {
337
+ throw new Error("prepareTweakDeposit is self-deposit only: the recipient must be able to derive the " +
338
+ "ephemeral key to recover the coins, and only this wallet's viewing key can");
339
+ }
340
+ const network = opts.network ?? sdkBitcoinNetworkToAddressNetwork(this.config.bitcoinNetwork);
341
+ return createTweakDeposit(meta, opts.ikaXOnlyPubkey, {
342
+ viewingNode: depositViewingNode(this._keys.viewingPrivKey),
343
+ depositIndex: opts.depositIndex,
344
+ }, network);
345
+ }
311
346
  /**
312
347
  * Prepare a BTC deposit: generate stealth deposit address + OP_RETURN.
313
348
  * Returns the deposit result (btcAddress, opReturnPayload) ready for PSBT building.
@@ -20,6 +20,20 @@ export declare function ed25519GenerateKeyPair(): {
20
20
  privKey: Uint8Array;
21
21
  pubKey: Uint8Array;
22
22
  };
23
+ /**
24
+ * Derive an Ed25519 keypair deterministically from secret material.
25
+ *
26
+ * Used for deposit ephemeral keys, where a random keypair is a liability: the
27
+ * deposit address commits to the ephemeral pubkey, so losing it makes the coins
28
+ * unspendable by anyone, forever. Deriving it from the owner's seed means the
29
+ * seed alone can rebuild every address they were ever handed.
30
+ *
31
+ * @param material - secret bytes; hashed, so it need not be uniform
32
+ */
33
+ export declare function ed25519KeyPairFromMaterial(material: Uint8Array): {
34
+ privKey: Uint8Array;
35
+ pubKey: Uint8Array;
36
+ };
23
37
  /**
24
38
  * Derive an Ed25519 public key from a private key
25
39
  *
@@ -27,6 +27,20 @@ export function ed25519GenerateKeyPair() {
27
27
  const pubKey = ed25519.getPublicKey(privKey);
28
28
  return { privKey, pubKey };
29
29
  }
30
+ /**
31
+ * Derive an Ed25519 keypair deterministically from secret material.
32
+ *
33
+ * Used for deposit ephemeral keys, where a random keypair is a liability: the
34
+ * deposit address commits to the ephemeral pubkey, so losing it makes the coins
35
+ * unspendable by anyone, forever. Deriving it from the owner's seed means the
36
+ * seed alone can rebuild every address they were ever handed.
37
+ *
38
+ * @param material - secret bytes; hashed, so it need not be uniform
39
+ */
40
+ export function ed25519KeyPairFromMaterial(material) {
41
+ const privKey = sha256(material);
42
+ return { privKey, pubKey: ed25519.getPublicKey(privKey) };
43
+ }
30
44
  /**
31
45
  * Derive an Ed25519 public key from a private key
32
46
  *
@@ -34,7 +34,7 @@ export { fetchTokenConfig, getTokenId, fetchSupportedTokens, fetchEnabledTokens,
34
34
  export { MAGICBLOCK_DELEGATION_PROGRAM_ID, MAGICBLOCK_DEVNET_ROUTER_URL, MAGICBLOCK_DEVNET_ROUTER_WS_URL, MAGICBLOCK_EPHEMERAL_VAULT_ID, MAGICBLOCK_MAGIC_CONTEXT_ID, MAGICBLOCK_MAGIC_PROGRAM_ID, MAGICBLOCK_MAX_PER_MEMBERS, MAGICBLOCK_PERMISSION_PROGRAM_ID, MAGICBLOCK_PER_MEMBER_FLAGS, MAGICBLOCK_VALIDATOR_IDENTITIES, buildDefaultPrivacyDomain, buildMagicBlockPerMemberFlags, deriveMagicBlockCommitRecordPDA, deriveMagicBlockCommitStatePDA, deriveMagicBlockDelegateBufferPDA, deriveMagicBlockDelegationMetadataPDA, deriveMagicBlockDelegationRecordPDA, deriveMagicBlockPermissionPDA, deriveMagicBlockUndelegateBufferPDA, requiresMagicBlockEndpoint, getMagicBlockEndpoint, getMagicBlockValidatorIdentity, createMagicBlockRouterConnection, type BuildPrivacyDomainOptions, type MagicBlockEndpointConfig, type MagicBlockExecutionMode, type MagicBlockPolicyMode, type MagicBlockPerMemberFlagName, type MagicBlockValidatorRegion, type PrivacyDomainConfig, type PrivacyDomainKind, } from "./magicblock";
35
35
  export { generateNote, createNoteFromSecrets, serializeNote, deserializeNote, noteHasComputedHashes, getNotePublicKeyX, computeNoteCommitment, computeNoteNullifier, formatBtc, parseBtc, deriveNote, deriveNotes, deriveMasterKey, deriveNoteFromMaster, estimateSeedStrength, createNote, prepareWithdrawal, createStealthNote, serializeStealthNote, deserializeStealthNote, type Note, type SerializedNote, type NoteData, type StealthNote, type SerializedStealthNote, createJoinSplitNote, computeJoinSplitNoteNullifier, serializeJoinSplitNote, deserializeJoinSplitNote, type JoinSplitNote, type SerializedJoinSplitNote, } from "./note";
36
36
  export { createMerkleProof, createMerkleProofFromBigints, proofToCircomFormat, proofToOnChainFormat, createEmptyMerkleProof, leafIndexToPathIndices, pathIndicesToLeafIndex, validateMerkleProofStructure, parseMerkleProofResponse, TREE_DEPTH, ROOT_HISTORY_SIZE, MAX_LEAVES, ZERO_VALUE, type MerkleProof, } from "./merkle";
37
- export { deriveTaprootAddress, deriveTaprootAddressWithRefund, depositTweakCommitment, buildRefundScript, computeTapLeafHash, verifyTaprootAddress, createP2TRScriptPubkey, parseP2TRScriptPubkey, isValidBitcoinAddress, getInternalKey, createCustomInternalKey, createOpReturnScriptFromPayload, buildDepositOpReturn, parseDepositOpReturn, encodeDepositOpReturnHeader, decodeDepositOpReturnHeader, validateDepositOpReturnContext, computeDepositPoolTag, DEPOSIT_DESTINATION_CHAIN, DEPOSIT_BITCOIN_NETWORK, DEPOSIT_OP_RETURN_VERSION, DEPOSIT_POOL_TAG_SIZE, DEPOSIT_OP_RETURN_SIZE, type DepositDestinationChain, type DepositBitcoinNetwork, type DepositOpReturnContext, type ParsedDepositOpReturn, } from "./taproot";
37
+ export { deriveTaprootAddress, deriveTaprootAddressWithRefund, depositTweakCommitment, deriveDepositAddress, depositLeafScript, DEPOSIT_NUMS_INTERNAL_KEY, buildRefundScript, computeTapLeafHash, verifyTaprootAddress, createP2TRScriptPubkey, parseP2TRScriptPubkey, isValidBitcoinAddress, getInternalKey, createCustomInternalKey, createOpReturnScriptFromPayload, buildDepositOpReturn, parseDepositOpReturn, encodeDepositOpReturnHeader, decodeDepositOpReturnHeader, validateDepositOpReturnContext, computeDepositPoolTag, DEPOSIT_DESTINATION_CHAIN, DEPOSIT_BITCOIN_NETWORK, DEPOSIT_OP_RETURN_VERSION, DEPOSIT_POOL_TAG_SIZE, DEPOSIT_OP_RETURN_SIZE, type DepositDestinationChain, type DepositBitcoinNetwork, type DepositOpReturnContext, type ParsedDepositOpReturn, } from "./taproot";
38
38
  export { encodeClaimLink, decodeClaimLink, parseClaimUrl, } from "./claim-link";
39
39
  export type { ProofData, MerkleProofInput, CircuitType, JoinSplitProofInputs, } from "./prover/web";
40
40
  export { uploadTransactionToBuffer, uploadProofToBuffer, closeBuffer, readBufferData, fetchRawTransaction, fetchMerkleProof, prepareVerifyDeposit, buildMerkleProof, needsBuffer as bufferNeedsBuffer, getProofSource, calculateUploadTransactions, CHADBUFFER_PROGRAM_ID, AUTHORITY_SIZE, MAX_DATA_PER_WRITE, SOLANA_TX_SIZE_LIMIT, type ProofUploadResult, } from "./chadbuffer";
@@ -44,7 +44,7 @@ export { getConfig, setConfig, createConfig, initConfig, DEVNET_CONFIG, DEVNET_R
44
44
  export { POOL_STATE_DISCRIMINATOR, POOL_STATE_LEN, POOL_STATE_OFFSETS, POOL_FLAG, parsePoolState, parsePoolFees, BPS_DENOMINATOR, computeBpsFee, feeShareBps, } from "./pool-state";
45
45
  export type { PoolState, PoolFees } from "./pool-state";
46
46
  export { PDA_SEEDS, poolStateSeeds, commitmentTreeSeeds, tokenConfigSeeds, poolConfigSeeds, nullifierRecordSeeds, redemptionRequestSeeds, vkRegistrySeeds, depositReceiptSeeds, policyApprovalSeeds, exitDestinationSeeds, lightClientSeeds, blockHeaderSeeds, heightIndexSeeds, verifiedTransactionSeeds, derivePoolStatePDA, deriveCommitmentTreePDA, deriveNullifierRecordPDA, derivePolicyApprovalPDA, deriveExitDestinationPDA, EXIT_KIND_SOLANA_OWNER, EXIT_KIND_BTC_SCRIPT, deriveLightClientPDA, deriveBlockHeaderPDA, deriveHeightIndexPDA, deriveVkRegistryPDA, deriveRedemptionRequestPDA, deriveTokenConfigPDA, derivePoolConfigPDA, deriveDepositReceiptPDA, commitmentToBytes, } from "./pda";
47
- export { createStealthDeposit, createStealthDepositWithKeys, createStealthOutput, createStealthOutputWithKeys, createStealthOutputForCommitment, packStealthOutputForCircuit, scanAnnouncements, scanAnnouncementsViewOnly, scanAnnouncementsViewOnlyMulti, exportViewOnlyKeys, encodeViewOnlyKeys, decodeViewOnlyKeys, prepareClaimInputs, scanUnifiedNotes, scanUnifiedNotesMulti, encryptAmount, decryptAmount, computeNullifierHashForNote, computeNullifierBytes, parseAnnouncementsFromHex, createDepositFromConfig, createDirectVaultDeposit, createTweakDeposit, isDepositForViewer, isDepositForViewerHex, ANNOUNCEMENT_TYPE_DEPOSIT, ANNOUNCEMENT_TYPE_TRANSFER, type StealthDeposit, type StealthOutputData, type StealthOutputWithKeys, type CircuitStealthOutput, type ScannedNote, type ClaimInputs as StealthClaimInputs, type OnChainStealthAnnouncement, type ConnectionAdapter, type ViewOnlyKeys, type ViewOnlyScannedNote, createNonInteractiveDeposit, pickIkaCustodyKey, type NonInteractiveDepositResult, type TweakDepositResult, type NonInteractiveDepositWithRefundResult, } from "./stealth";
47
+ export { createStealthDeposit, createStealthDepositWithKeys, createStealthOutput, createStealthOutputWithKeys, createStealthOutputForCommitment, packStealthOutputForCircuit, scanAnnouncements, scanAnnouncementsViewOnly, scanAnnouncementsViewOnlyMulti, exportViewOnlyKeys, encodeViewOnlyKeys, decodeViewOnlyKeys, prepareClaimInputs, scanUnifiedNotes, scanUnifiedNotesMulti, encryptAmount, decryptAmount, computeNullifierHashForNote, computeNullifierBytes, parseAnnouncementsFromHex, createDepositFromConfig, createDirectVaultDeposit, createTweakDeposit, depositEphemeralKeyPair, depositViewingNode, outgoingViewingNode, outgoingEphemeralKeyPair, findNextSendIndex, isDepositForViewer, isDepositForViewerHex, ANNOUNCEMENT_TYPE_DEPOSIT, ANNOUNCEMENT_TYPE_TRANSFER, type StealthDeposit, type StealthOutputData, type StealthOutputWithKeys, type CircuitStealthOutput, type ScannedNote, type ClaimInputs as StealthClaimInputs, type OnChainStealthAnnouncement, type ConnectionAdapter, type ViewOnlyKeys, type ViewOnlyScannedNote, createNonInteractiveDeposit, pickIkaCustodyKey, type NonInteractiveDepositResult, type TweakDepositResult, type DepositRecoveryMaterial, type OutgoingRecoveryMaterial, type NonInteractiveDepositWithRefundResult, } from "./stealth";
48
48
  export { buildDepositPsbt, estimateDepositFee, fetchUtxos, selectUtxos, type BuildDepositPsbtParams, type BuildDepositPsbtResult, type UtxoDescriptor, } from "./psbt";
49
49
  export { EsploraClient, esploraTestnet, esploraMainnet, type EsploraTransaction, type EsploraVin, type EsploraVout, type EsploraStatus, type EsploraAddressInfo, type EsploraUtxo, type EsploraMerkleProof, type EsploraNetwork, } from "./core/esplora";
50
50
  export { MempoolClient, mempoolTestnet, mempoolMainnet, reverseBytes, type BlockHeader, type TransactionInfo, type SPVProofData, } from "./core/mempool";
@@ -53,7 +53,7 @@ export { setDebug } from "./logger";
53
53
  export { createFetchConnectionAdapter, createConnectionAdapterFromWeb3, createConnectionAdapterFromKit, getConnectionAdapter, clearConnectionAdapterCache, type RpcConfig, type Web3Connection, type KitRpc, } from "./solana/connection";
54
54
  export { resolveSnsName, resolveStealthName, parseSnsStealthData, isSnsStealthAddress, isAuditorDisclosable, SnsComplianceFlags, SNS_COMPLIANCE_AUDITOR_OFFSET, SNS_COMPLIANCE_AUDITOR_BYTES, deriveParentDomainKey, SNS_STEALTH_DATA_SIZE, type SnsStealthAddress, } from "./sns-resolver";
55
55
  export { COMMITMENT_TREE_DISCRIMINATOR, parseCommitmentTreeData, isValidRoot, fetchCommitmentTree, getCommitmentIndex, saveCommitmentIndex, CommitmentTreeIndex, buildCommitmentTreeFromChain, fetchLeafIndexForCommitment, fetchMerkleProofForCommitment, getMerkleProofFromTree, type CommitmentTreeState, type RpcClient, type OnChainMerkleProof, } from "./commitment-tree";
56
- export { INSTRUCTION_DISCRIMINATORS, buildShieldInstructionData, buildShieldInstruction, type ShieldInstructionOptions, buildApproveRedemptionSigningInstructionData, buildApproveRedemptionSigningInstruction, buildCancelRedemptionInstructionData, buildCancelRedemptionInstruction, type CancelRedemptionInstructionOptions, bigintTo32Bytes, bytes32ToBigint, buildTransactInstructionData, buildTransactInstruction, buildRedeemInstructionData, buildUnshieldInstructionData, buildUnshieldInstruction, buildProposePoolUpdateInstructionData, buildProposePoolUpdateInstruction, buildExecutePoolUpdateInstructionData, buildExecutePoolUpdateInstruction, buildCancelPoolUpdateInstructionData, buildCancelPoolUpdateInstruction, buildRotateTreeInstructionData, buildRotateTreeInstruction, type RotateTreeOptions, buildMagicBlockDelegateInstructionData, buildMagicBlockDelegateInstruction, buildMagicBlockCommitInstructionData, buildMagicBlockCommitInstruction, buildMagicBlockPerPermissionInstructionData, buildMagicBlockPerPermissionInstruction, buildPolicyRequestHash, buildPolicyIntentParts, buildRegisterExitDestinationInstruction, buildRegisterExitDestinationInstructionData, MAX_POLICY_INTENT_PARTS, buildInitializePolicyApprovalInstructionData, buildInitializePolicyApprovalInstruction, buildPolicyApprovalDecisionInstruction, buildPolicyApprovalCommitInstruction, buildCompleteDepositPermissionedInstructionData, buildCompleteDepositPermissionedInstruction, buildShieldPermissionedInstructionData, buildShieldPermissionedInstruction, buildRotateAuditorInstructionData, buildRotateAuditorInstruction, type MagicBlockDelegateTarget, type MagicBlockDelegateInstructionOptions, type MagicBlockCommitInstructionOptions, type MagicBlockPerPermissionOperation, type MagicBlockPerPermissionMember, type MagicBlockPerPermissionInstructionOptions, type PolicyApprovalDecision, type InitializePolicyApprovalOptions, type CompleteDepositPermissionedOptions, type ShieldPermissionedInstructionOptions, type RotateAuditorOptions, buildVerifyTransactionInstructionData, buildCompleteDepositInstructionData, buildVerifyDepositInstructionData, buildSetPoolConfigInstructionData, parsePoolConfig, type ParsedPoolConfig, POOL_CONFIG_DISCRIMINATOR, POOL_CONFIG_LEN, POOL_SCRIPT_MAX_LEN, type Instruction, type ApproveRedemptionSigningInstructionOptions, type TransactInstructionOptions, type UnshieldInstructionOptions, type ProposePoolUpdateOptions, type ExecutePoolUpdateOptions, type CancelPoolUpdateOptions, } from "./instructions";
56
+ export { INSTRUCTION_DISCRIMINATORS, buildShieldInstructionData, buildShieldInstruction, type ShieldInstructionOptions, buildApproveRedemptionSigningInstructionData, buildApproveRedemptionSigningInstruction, buildCancelRedemptionInstructionData, buildCancelRedemptionInstruction, type CancelRedemptionInstructionOptions, bigintTo32Bytes, bytes32ToBigint, buildTransactInstructionData, buildTransactInstruction, buildRedeemInstructionData, buildUnshieldInstructionData, buildUnshieldInstruction, buildProposePoolUpdateInstructionData, buildProposePoolUpdateInstruction, buildExecutePoolUpdateInstructionData, buildExecutePoolUpdateInstruction, buildCancelPoolUpdateInstructionData, buildCancelPoolUpdateInstruction, buildRotateTreeInstructionData, buildRotateTreeInstruction, type RotateTreeOptions, buildMagicBlockDelegateInstructionData, buildMagicBlockDelegateInstruction, buildMagicBlockCommitInstructionData, buildMagicBlockCommitInstruction, buildMagicBlockPerPermissionInstructionData, buildMagicBlockPerPermissionInstruction, buildPolicyRequestHash, buildPolicyIntentParts, buildRegisterExitDestinationInstruction, buildRegisterExitDestinationInstructionData, MAX_POLICY_INTENT_PARTS, buildInitializePolicyApprovalInstructionData, buildInitializePolicyApprovalInstruction, buildPolicyApprovalDecisionInstruction, buildPolicyApprovalCommitInstruction, buildCompleteDepositPermissionedInstructionData, buildCompleteDepositPermissionedInstruction, buildShieldPermissionedInstructionData, buildShieldPermissionedInstruction, buildRotateAuditorInstructionData, buildRotateAuditorInstruction, type MagicBlockDelegateTarget, type MagicBlockDelegateInstructionOptions, type MagicBlockCommitInstructionOptions, type MagicBlockPerPermissionOperation, type MagicBlockPerPermissionMember, type MagicBlockPerPermissionInstructionOptions, type PolicyApprovalDecision, type InitializePolicyApprovalOptions, type CompleteDepositPermissionedOptions, type ShieldPermissionedInstructionOptions, type RotateAuditorOptions, buildVerifyTransactionInstructionData, buildCompleteDepositInstructionData, buildVerifyDepositInstructionData, buildVerifyDepositPermissionedInstructionData, buildSetPoolConfigInstructionData, parsePoolConfig, type ParsedPoolConfig, POOL_CONFIG_DISCRIMINATOR, POOL_CONFIG_LEN, POOL_SCRIPT_MAX_LEN, type Instruction, type ApproveRedemptionSigningInstructionOptions, type TransactInstructionOptions, type UnshieldInstructionOptions, type ProposePoolUpdateOptions, type ExecutePoolUpdateOptions, type CancelPoolUpdateOptions, } from "./instructions";
57
57
  export { VK_REGISTRY_DISCRIMINATOR, VK_REGISTRY_LEN, MAX_IC_POINTS, MAX_SAFE_JOINSPLIT_SIZE, INIT_VK_REGISTRY_DISCRIMINATOR, UPDATE_VK_REGISTRY_DISCRIMINATOR, joinSplitNumPublicInputs, computeVkHash, vkeyJsonToVkMaterial, buildVkRegistryData, parseVkRegistry, assertVkRegistryForShape, isVkRegistryReady, type JoinSplitVkMaterial, type SnarkjsVkeyJson, type ParsedVkRegistry, } from "./vk-registry";
58
58
  export { fetchExplorerDeposits, fetchExplorerTransfers, fetchExplorerRedemptions, parseNullifierRecord, parseRedemptionRequest, NULLIFIER_RECORD_SIZE, REDEMPTION_REQUEST_SIZE, NULLIFIER_RECORD_DISCRIMINATOR, REDEMPTION_REQUEST_DISCRIMINATOR, OPERATION_TYPE_LABELS, type ExplorerDeposit, type ExplorerTransferEvent, type ExplorerRedemption, type IndexerLeaf, } from "./explorer";
59
59
  export { parseProgramEvents, parseNullifierSpentEvent, parseStealthAnnouncementEvent, parseSenderMemoEvent, parseBtcOriginAttestationEvent, parseAuditorCiphertextEvent, EVENT_NULLIFIER_SPENT, EVENT_STEALTH_ANNOUNCEMENT, EVENT_NULLIFIERS_BATCH, EVENT_ANNOUNCEMENTS_BATCH, EVENT_SENDER_MEMO, EVENT_BTC_ORIGIN_ATTESTATION, EVENT_AUDITOR_CIPHERTEXT, type NullifierSpentEvent, type StealthAnnouncementEvent, type SenderMemoEvent, type BtcOriginAttestationEvent, type AuditorCiphertextEvent, type ProgramEvent, } from "./events";
@@ -94,7 +94,7 @@ export { createMerkleProof, createMerkleProofFromBigints, proofToCircomFormat, p
94
94
  // ==========================================================================
95
95
  // Taproot address utilities
96
96
  // ==========================================================================
97
- export { deriveTaprootAddress, deriveTaprootAddressWithRefund, depositTweakCommitment, buildRefundScript, computeTapLeafHash, verifyTaprootAddress, createP2TRScriptPubkey, parseP2TRScriptPubkey, isValidBitcoinAddress, getInternalKey, createCustomInternalKey, createOpReturnScriptFromPayload, buildDepositOpReturn, parseDepositOpReturn, encodeDepositOpReturnHeader, decodeDepositOpReturnHeader, validateDepositOpReturnContext, computeDepositPoolTag, DEPOSIT_DESTINATION_CHAIN, DEPOSIT_BITCOIN_NETWORK, DEPOSIT_OP_RETURN_VERSION, DEPOSIT_POOL_TAG_SIZE, DEPOSIT_OP_RETURN_SIZE, } from "./taproot";
97
+ export { deriveTaprootAddress, deriveTaprootAddressWithRefund, depositTweakCommitment, deriveDepositAddress, depositLeafScript, DEPOSIT_NUMS_INTERNAL_KEY, buildRefundScript, computeTapLeafHash, verifyTaprootAddress, createP2TRScriptPubkey, parseP2TRScriptPubkey, isValidBitcoinAddress, getInternalKey, createCustomInternalKey, createOpReturnScriptFromPayload, buildDepositOpReturn, parseDepositOpReturn, encodeDepositOpReturnHeader, decodeDepositOpReturnHeader, validateDepositOpReturnContext, computeDepositPoolTag, DEPOSIT_DESTINATION_CHAIN, DEPOSIT_BITCOIN_NETWORK, DEPOSIT_OP_RETURN_VERSION, DEPOSIT_POOL_TAG_SIZE, DEPOSIT_OP_RETURN_SIZE, } from "./taproot";
98
98
  // ==========================================================================
99
99
  // Claim link utilities
100
100
  // ==========================================================================
@@ -127,7 +127,7 @@ poolStateSeeds, commitmentTreeSeeds, tokenConfigSeeds, poolConfigSeeds, nullifie
127
127
  // ==========================================================================
128
128
  // Stealth address utilities
129
129
  // ==========================================================================
130
- export { createStealthDeposit, createStealthDepositWithKeys, createStealthOutput, createStealthOutputWithKeys, createStealthOutputForCommitment, packStealthOutputForCircuit, scanAnnouncements, scanAnnouncementsViewOnly, scanAnnouncementsViewOnlyMulti, exportViewOnlyKeys, encodeViewOnlyKeys, decodeViewOnlyKeys, prepareClaimInputs, scanUnifiedNotes, scanUnifiedNotesMulti, encryptAmount, decryptAmount, computeNullifierHashForNote, computeNullifierBytes, parseAnnouncementsFromHex, createDepositFromConfig, createDirectVaultDeposit, createTweakDeposit, isDepositForViewer, isDepositForViewerHex, ANNOUNCEMENT_TYPE_DEPOSIT, ANNOUNCEMENT_TYPE_TRANSFER, createNonInteractiveDeposit, pickIkaCustodyKey, } from "./stealth";
130
+ export { createStealthDeposit, createStealthDepositWithKeys, createStealthOutput, createStealthOutputWithKeys, createStealthOutputForCommitment, packStealthOutputForCircuit, scanAnnouncements, scanAnnouncementsViewOnly, scanAnnouncementsViewOnlyMulti, exportViewOnlyKeys, encodeViewOnlyKeys, decodeViewOnlyKeys, prepareClaimInputs, scanUnifiedNotes, scanUnifiedNotesMulti, encryptAmount, decryptAmount, computeNullifierHashForNote, computeNullifierBytes, parseAnnouncementsFromHex, createDepositFromConfig, createDirectVaultDeposit, createTweakDeposit, depositEphemeralKeyPair, depositViewingNode, outgoingViewingNode, outgoingEphemeralKeyPair, findNextSendIndex, isDepositForViewer, isDepositForViewerHex, ANNOUNCEMENT_TYPE_DEPOSIT, ANNOUNCEMENT_TYPE_TRANSFER, createNonInteractiveDeposit, pickIkaCustodyKey, } from "./stealth";
131
131
  // ==========================================================================
132
132
  // PSBT builder for wallet-integrated deposits
133
133
  // ==========================================================================
@@ -181,7 +181,7 @@ buildRotateTreeInstructionData, buildRotateTreeInstruction,
181
181
  // MagicBlock lifecycle
182
182
  buildMagicBlockDelegateInstructionData, buildMagicBlockDelegateInstruction, buildMagicBlockCommitInstructionData, buildMagicBlockCommitInstruction, buildMagicBlockPerPermissionInstructionData, buildMagicBlockPerPermissionInstruction, buildPolicyRequestHash, buildPolicyIntentParts, buildRegisterExitDestinationInstruction, buildRegisterExitDestinationInstructionData, MAX_POLICY_INTENT_PARTS, buildInitializePolicyApprovalInstructionData, buildInitializePolicyApprovalInstruction, buildPolicyApprovalDecisionInstruction, buildPolicyApprovalCommitInstruction, buildCompleteDepositPermissionedInstructionData, buildCompleteDepositPermissionedInstruction, buildShieldPermissionedInstructionData, buildShieldPermissionedInstruction, buildRotateAuditorInstructionData, buildRotateAuditorInstruction,
183
183
  // Verify instruction data builders
184
- buildVerifyTransactionInstructionData, buildCompleteDepositInstructionData, buildVerifyDepositInstructionData,
184
+ buildVerifyTransactionInstructionData, buildCompleteDepositInstructionData, buildVerifyDepositInstructionData, buildVerifyDepositPermissionedInstructionData,
185
185
  // Pool config (disc 2) builder + parser
186
186
  buildSetPoolConfigInstructionData, parsePoolConfig, POOL_CONFIG_DISCRIMINATOR, POOL_CONFIG_LEN, POOL_SCRIPT_MAX_LEN, } from "./instructions";
187
187
  // ==========================================================================