veilix-sdk 0.0.10 → 0.2.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
@@ -2,6 +2,8 @@
2
2
 
3
3
  TypeScript SDK for interacting with the Veilix privacy protocol on Solana — deposits, withdrawals, private transfers, and fee quotes.
4
4
 
5
+ Veilix uses a UTXO note model: a deposit can be **any amount** (up to the pool's per-deposit cap), and a withdrawal can take the whole note or only part of it — the remainder comes back as a new *change note*.
6
+
5
7
  **Third-party partners** integrating Private Send into their own website or app (quote → wallet sign → deposit → `/submit`): see [`docs/partner-private-send.md`](./docs/partner-private-send.md).
6
8
 
7
9
  ## Installation
@@ -18,10 +20,10 @@ npm install @solana/wallet-adapter-react react
18
20
 
19
21
  ## Prerequisites
20
22
 
21
- - A Solana wallet (`Keypair` for backend/scripts, or a wallet adapter with `signMessage` / `signAllTransactions` for frontend)
23
+ - A Solana wallet (`Keypair` for backend/scripts, or a wallet adapter with `signMessage` / `signTransaction` / `signAllTransactions` for frontend)
22
24
  - An RPC endpoint or existing `@solana/web3.js` `Connection`
23
25
 
24
- Supported networks: `mainnet` and `devnet` (inferred from the RPC URL, or set explicitly via `network`).
26
+ Supported networks: `mainnet` and `devnet` (set via `network`).
25
27
 
26
28
  ---
27
29
 
@@ -29,244 +31,216 @@ Supported networks: `mainnet` and `devnet` (inferred from the RPC URL, or set ex
29
31
 
30
32
  Create one SDK instance per configuration (network, RPC, wallet). Initialization loads active relayers for the target network.
31
33
 
32
- ```ts, tsx
33
- import { VeilixSDK } from "veilix-sdk";
34
+ ```ts
35
+ import { VeilixSDK } from "veilix-sdk";
34
36
 
35
- VeilixSDK.create({
36
- rpcEndpoint: RPC_URL,
37
+ const sdk = await VeilixSDK.create({
38
+ rpcEndpoint: RPC_URL, // or `connection`
37
39
  wallet,
38
- network: NETWORK,
39
- })
40
- .then((sdk) => {
41
- ...
42
- })
40
+ network: "mainnet",
41
+ });
43
42
  ```
44
43
 
45
44
  ### Init options
46
45
 
47
46
  | Option | Required | Description |
48
47
  |--------|----------|-------------|
49
- | `wallet` | yes | Anchor-compatible wallet (`Keypair` or wallet adapter) |
50
- | `rpcEndpoint` | yes | Solana JSON-RPC URL |
51
- | `network` | yes | `"mainnet"` or `"devnet"` — override when the RPC URL has no cluster hint |
48
+ | `wallet` | yes | Anchor-compatible wallet (`Keypair` wrapper or wallet adapter) |
49
+ | `rpcEndpoint` / `connection` | one of | Solana JSON-RPC URL, or an existing `Connection` |
50
+ | `network` | yes | `"mainnet"` or `"devnet"` |
51
+ | `circuitPathsBase` | no | Self-hosted location of `withdraw.wasm` / `withdraw.zkey` |
52
+ | `indexerUrl` | no | Indexer override (defaults to the Veilix indexer of `network`) |
53
+ | `riskApiUrl` | no | Address-risk API override |
52
54
 
53
55
  After creation, inspect runtime config via `sdk.config` or helpers like `sdk.getNetwork()`, `sdk.relayers`, and `sdk.getTokenMints()`.
54
56
 
55
57
  ---
56
58
 
57
- ## `getEncryptionKey`
59
+ ## Notes
60
+
61
+ A note is the secret needed to spend deposited funds:
62
+
63
+ ```
64
+ vx2-<amountHex>-<privateKeyHex>-<blindingHex>[-<mint>]
65
+ ```
66
+
67
+ The mint suffix is omitted for SOL. Anyone holding the note can withdraw it, so store it safely. Notes from the previous (fixed-denomination) pool are not compatible; `sdk.isLegacyNote(note)` detects them.
58
68
 
59
- Derives a note-encryption key from a wallet signature and stores it on the SDK instance (`sdk.encryptKey`). **Required before any deposit that uses on-chain note backup** (`onChainBackup: true`, including `veilixTransfer`).
69
+ ```ts
70
+ const parsed = sdk.parseSecretNote(note); // { amount, mint, commitment, commitmentHex, ... }
71
+ const status = await sdk.getNoteStatus(note); // { deposited, index, spent, spentIn, ... }
72
+ ```
60
73
 
61
- The wallet signs the message `veilix:notes:v1`. Accepts a backend `Keypair` or a frontend wallet adapter with `signMessage`.
74
+ ---
75
+
76
+ ## `getEncryptionKey`
77
+
78
+ Derives a note-encryption key from a wallet signature and stores it on the SDK instance (`sdk.encryptKey`). The wallet signs the message `veilix:notes:v1`. Accepts a backend `Keypair` or a frontend wallet adapter with `signMessage`.
62
79
 
63
80
  ```ts
64
81
  await sdk.getEncryptionKey(wallet);
65
- // sdk.encryptKey is now set
66
82
  ```
67
83
 
68
- The key is used to encrypt secret notes before they are stored on-chain via `backupNotes`, so you can recover notes later from the same wallet.
84
+ The key is used to:
69
85
 
70
- ---
86
+ - seal a deposit's note into its on-chain encrypted output (`onChainBackup: true`), and
87
+ - seal the change note of a partial withdrawal,
71
88
 
72
- ## `buildDepositTx`
89
+ so that `sdk.scanOwnedNotes()` can recover every unspent note of the wallet later.
90
+
91
+ ---
73
92
 
74
- Builds an unsigned deposit transaction for a pre-generated secret note.
93
+ ## Deposit
75
94
 
76
- ### 1. Generate deposit parameters
95
+ ### 1. Generate a note
77
96
 
78
97
  ```ts
79
- // 1 SOL deposit (lamports)
80
- const { note } = sdk.generateDepositParams(1_000_000_000n, null);
98
+ // 1.5 SOL (lamports)
99
+ const { note } = sdk.generateDepositParams(1_500_000_000n, null);
81
100
 
82
- // SPL token deposit — pass the token mint
101
+ // SPL token — pass the token mint and base units
83
102
  const usdcMint = sdk.getTokenMint("USDC");
84
- const { note: usdcNote } = sdk.generateDepositParams(1_000_000n, usdcMint);
103
+ const { note: usdcNote } = sdk.generateDepositParams(
104
+ sdk.parseAmountToBaseUnits("25.5", 6),
105
+ usdcMint
106
+ );
85
107
  ```
86
108
 
87
- Deposit amounts must match [supported pool denominations](#supported-deposit-denominations) for each token.
88
-
89
- ### 2. Build the transaction
109
+ ### 2. Build, sign and send
90
110
 
91
111
  ```ts
92
112
  await sdk.getEncryptionKey(wallet); // required when onChainBackup is true
93
113
 
94
- const { tx, note } = await sdk.buildDepositTx({
114
+ const tx = await sdk.buildDepositTx({
95
115
  senderPublicKey: wallet.publicKey,
96
116
  note,
97
- onChainBackup: true, // encrypt and store note on-chain (recommended)
98
- computeUnitPrice: 50_000, // optional priority fee
99
- tempSigner: tempSignerPublicKey // to prevent ix assertion in browser
117
+ onChainBackup: true, // recover the note later with scanOwnedNotes (recommended)
118
+ computeUnitPrice: 50_000, // optional priority fee (micro-lamports / CU)
100
119
  });
120
+
121
+ const signed = await wallet.signTransaction(tx); // VersionedTransaction
122
+ const signature = await connection.sendRawTransaction(signed.serialize());
101
123
  ```
102
124
 
103
- Sign and send the returned `tx` with your wallet. **Save the returned `note`** — it is required to withdraw later.
125
+ `buildDepositTx` generates the deposit ZK proof (a few seconds) and returns an unsigned `VersionedTransaction`. **Save the `note` before sending** — it is required to withdraw.
104
126
 
105
127
  | Parameter | Description |
106
128
  |-----------|-------------|
107
129
  | `senderPublicKey` | Depositor's public key |
108
130
  | `note` | Secret note from `generateDepositParams` |
109
- | `onChainBackup` | When `true`, encrypts the note and adds a `backupNotes` instruction (requires `getEncryptionKey`) |
110
- | `computeUnitPrice` | Priority fee in micro-lamports (default `50000`) |
131
+ | `onChainBackup` | Seal the note into the deposit's encrypted output (requires `getEncryptionKey`) |
132
+ | `computeUnitPrice` | Priority fee in micro-lamports (default `5000`) |
133
+ | `computeUnitLimit` | Compute unit limit override |
134
+
135
+ The pool's per-deposit cap is available via `(await sdk.fetchTreeAccount(mint)).maxDepositAmount`.
111
136
 
112
137
  ---
113
138
 
114
139
  ## `withdraw`
115
140
 
116
- Withdraws funds from the privacy pool to a recipient using a secret note from a prior deposit. The SDK:
141
+ Withdraws a note (or part of it) to a recipient through a relayer. The SDK:
117
142
 
118
143
  1. Validates the recipient against the risk API
119
- 2. Fetches Merkle commitments from the indexer
120
- 3. Generates a ZK withdraw proof
121
- 4. Submits the proof to a relayer (chosen at random, or by index)
144
+ 2. Locates the note in the indexer and checks it is unspent
145
+ 3. Rebuilds the Merkle tree and generates the ZK proof
146
+ 4. Submits the proof to a relayer (random, by index, or by URL)
122
147
  5. Polls until the relayer confirms the on-chain transaction
123
148
 
124
149
  ```ts
125
- const result = await sdk.withdraw({
126
- note: "veilix-1-...", // secret note from deposit
127
- recipient: "RecipientPubkey...", // withdrawal destination
128
- },
129
- relayerIdx: -1, // optional — random relayer when omitted
130
- );
150
+ // Full note
151
+ const result = await sdk.withdraw({ note, recipient: "RecipientPubkey..." });
152
+
153
+ // Partial: withdraw 0.4 SOL, keep the rest as a change note
154
+ const partial = await sdk.withdraw({
155
+ note,
156
+ recipient: "RecipientPubkey...",
157
+ amount: 400_000_000n,
158
+ });
159
+ console.log(partial.changeNote); // save it — it holds the remaining funds
131
160
 
132
- console.log(result.txHash); // on-chain transaction signature
133
- console.log(result.status); // "success" | "failed"
161
+ console.log(result.status); // "success" | "failed"
162
+ console.log(result.txHash); // withdraw transaction signature
163
+ console.log(result.fee); // total fee in base units (relayer + platform)
134
164
  ```
135
165
 
136
166
  | Parameter | Description |
137
167
  |-----------|-------------|
138
- | `note` | Secret note from the original deposit |
168
+ | `note` | Secret note to spend |
139
169
  | `recipient` | Recipient public key (`PublicKey` or base58 string) |
140
- | `relayerIdx` | Optional index into `sdk.relayers` |
170
+ | `amount` | Optional base-unit amount before fees; defaults to the full note |
171
+ | `relayerUrl` | Optional relayer URL (otherwise `relayerIdx` or random) |
172
+ | second argument `relayerIdx` | Optional index into `sdk.relayers` |
173
+
174
+ The recipient receives `amount - fee`. When `sdk.encryptKey` is set, the change note is also sealed on-chain so `scanOwnedNotes` can find it.
175
+
176
+ ---
177
+
178
+ ## `scanOwnedNotes`
179
+
180
+ Recovers the wallet's notes by trial-decrypting the pool's encrypted outputs with `sdk.encryptKey`:
181
+
182
+ ```ts
183
+ await sdk.getEncryptionKey(wallet);
184
+ const notes = await sdk.scanOwnedNotes(); // unspent notes, SOL + configured tokens
185
+ // options: { mints: [null, usdcMint], includeSpent: true }
186
+ ```
141
187
 
142
- Withdraw fees are calculated automatically from the relayer fee and on-chain platform fee.
188
+ Each entry is a `NoteStatus` with the re-encoded `note`, `amount`, `mint`, `index` and spend state.
143
189
 
144
190
  ---
145
191
 
146
192
  ## `veilixTransfer`
147
193
 
148
- End-to-end private transfer: deposit into the pool (split across supported denominations), then withdraw each successful note to the recipient. Handles signing, sending, and settlement automatically.
194
+ End-to-end private transfer: deposits one note per item, then withdraws each note to its recipient via the indexer `/submit` queue. Handles proving, signing, sending, and settlement.
149
195
 
150
- **Requires keep open the window till get response for correct work**
196
+ **Keep the window open until it resolves.**
151
197
 
152
198
  ```ts
153
199
  import { PublicKey } from "@solana/web3.js";
154
200
 
155
201
  const result = await sdk.veilixTransfer(
156
202
  [
157
- {
158
- mint: new PublicKey(sdk.getTokenMint("USDC")),
159
- amount: "25", // human-readable amount
160
- recipient: new PublicKey("RecipientPubkey..."),
161
- },
162
- {
163
- mint: new PublicKey(sdk.getTokenMints().Solana),
164
- amount: "1.5",
165
- recipient: new PublicKey("AnotherRecipient..."),
166
- },
203
+ { mint: new PublicKey(sdk.getTokenMint("USDC")), amount: "25", recipient: new PublicKey("Recipient...") },
204
+ { mint: new PublicKey(sdk.getTokenMints().Solana), amount: "1.5", recipient: new PublicKey("Another...") },
167
205
  ],
206
+ { uniqueRelayer: false, onChainBackup: true, downloadNotes: true }
168
207
  );
169
208
 
170
209
  if (result.error) {
171
210
  console.log("Deposit failures:", result.depositFailed);
172
- console.log("Withdraw failures:", result.withdrawFailed);
211
+ console.log("Withdraw failures:", result.withdrawFailed); // include the note, still spendable
173
212
  }
174
213
  ```
175
214
 
176
- | Field | Description |
177
- |-------|-------------|
178
- | `items[].mint` | Token mint to transfer |
179
- | `items[].amount` | Human-readable amount (e.g. `"1.5"` SOL) |
180
- | `items[].recipient` | Withdrawal destination for that transfer |
215
+ | Option | Default | Description |
216
+ |--------|---------|-------------|
217
+ | `uniqueRelayer` | `false` | Use one random relayer for every withdraw |
218
+ | `onChainBackup` | `true` when `encryptKey` is set | Seal each note into its deposit |
219
+ | `downloadNotes` | `true` | Download the notes as CSV (browser) before sending |
181
220
 
182
- The SDK splits each amount into the nearest supported deposit denominations, builds and sends deposit transactions, waits for settlement, then withdraws each note to its recipient. Partial failures are reported in `depositFailed` and `withdrawFailed` without throwing.
221
+ Partial failures are reported without throwing; `result.notes` lists every note created.
183
222
 
184
223
  ---
185
224
 
186
225
  ## `getQuote`
187
226
 
188
- Fetches a withdraw fee quote from the Veilix indexer before executing a withdrawal. Returns estimated relayer fees, recipient amount, ETA, and how the amount splits across denominations.
227
+ Fetches a withdraw fee quote from the indexer.
189
228
 
190
229
  ```ts
191
230
  const { quote } = await sdk.getQuote({
192
- amount: 1.5, // human-readable amount
193
- amountLamports: 1_500_000_000, // same amount in smallest units
194
- recipient: "RecipientPubkey...",
195
- asset: "SOL", // optional — default "SOL"
196
- });
197
-
198
- console.log(quote.recipientAmount); // amount recipient receives after fees
199
- console.log(quote.totalRelayerFee);
200
- console.log(quote.etaSeconds);
201
- console.log(quote.splits); // per-denomination breakdown
202
- console.log(quote.platformFee); // platform fee (added by SDK)
203
- ```
204
-
205
- | Parameter | Description |
206
- |-----------|-------------|
207
- | `amount` | Human-readable transfer/withdraw amount |
208
- | `amountLamports` | Amount in smallest token units |
209
- | `recipient` | Intended withdrawal recipient |
210
- | `asset` | Indexer asset label (default `"SOL"`) |
211
- | `network` | Optional cluster override |
212
-
213
- `getQuote` is also exported as a standalone function but requires an initialized SDK context.
214
-
215
- ---
216
-
217
- ## Typical flows
218
-
219
- ### Deposit and withdraw
220
-
221
- ```ts
222
- const sdk = await VeilixSDK.create({ rpcEndpoint, wallet, network: "mainnet" });
223
-
224
- await sdk.getEncryptionKey(wallet);
225
-
226
- const { note } = sdk.generateDepositParams(1_000_000_000n, null);
227
- const { tx } = await sdk.buildDepositTx({
228
- senderPublicKey: wallet.publicKey,
229
- note,
230
- onChainBackup: true,
231
+ amount: "1.5", // or amountRaw: "1500000000"
232
+ asset: "SOL", // "SOL" or a token mint
233
+ recipient: "Recipient...",
231
234
  });
232
235
 
233
- // sign and send tx...
234
-
235
- const { txHash } = await sdk.withdraw({
236
- note,
237
- recipient: "RecipientPubkey...",
238
- });
236
+ console.log(quote.recipientAmount, quote.fee, quote.relayerFee, quote.platformFee);
237
+ console.log(quote.relayer.url, quote.etaSeconds);
239
238
  ```
240
239
 
241
- ### Quote before withdraw
242
-
243
- ```ts
244
- const { quote } = await sdk.getQuote({
245
- amount: 1,
246
- amountLamports: 1_000_000_000,
247
- recipient: "RecipientPubkey...",
248
- });
249
-
250
- console.log(`Recipient receives ~${quote.recipientAmount} SOL`);
251
-
252
- await sdk.withdraw({ note, recipient: "RecipientPubkey..." });
253
- ```
240
+ For the exact fee the program will enforce, use `sdk.computeWithdrawFee(amount, relayerFeePercent, await sdk.fetchPlatformFeeWallets())`.
254
241
 
255
242
  ---
256
243
 
257
- ## Supported deposit denominations
258
-
259
- Amounts must decompose exactly into these pool denominations:
260
-
261
- | Token | Denominations | Mint Address (mainnet) |
262
- |-------|---------------|---------------|
263
- | SOL | 0.1, 1, 5, 10, 50, 100, 500 | |
264
- | USDC | 1, 10, 50, 100, 1000, 10000, 100000, 1000000 | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
265
- | Veilix | 10000,50000,100000,500000,1000000,10000000 | |
266
- | Bitcoin | 0.01, 0.05, 0.1, 0.5, 1, 5 | 3NZ9JMVBmGAqocybic2c7LQCJScmgsAZ6vQqTDzcqmJh |
267
- | Ethereum (coming soon) | 0.1, 1, 10, 50, 100, 500 | 7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs |
268
-
269
- Use `sdk.getDepositAmounts("Solana")` (or another symbol) to retrieve the list programmatically.
270
-
271
- ---
244
+ ## Circuit artifacts
272
245
 
246
+ In the browser the SDK loads `withdraw.wasm` / `withdraw.zkey` from the pinned CDN (SHA-256 verified) unless `circuitPathsBase` points elsewhere. With Vite, `veilix-sdk/vite` serves them from `node_modules` during development. In Node the files are read from the package's `dist/circuits`.