@velumdotcash/sdk 2.0.0 → 2.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/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # @velumdotcash/sdk
2
2
 
3
- TypeScript SDK for private payments on Solana using Zero-Knowledge proofs.
3
+ [![npm](https://img.shields.io/npm/v/@velumdotcash/sdk)](https://www.npmjs.com/package/@velumdotcash/sdk)
4
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
5
+
6
+ TypeScript SDK for private payments on Solana using Zero-Knowledge proofs. Deposit, withdraw, and transfer shielded funds with ZK-SNARKs.
4
7
 
5
8
  ## Installation
6
9
 
@@ -10,116 +13,125 @@ npm install @velumdotcash/sdk
10
13
 
11
14
  ## Quick Start
12
15
 
16
+ ### Browser (with wallet adapter)
17
+
13
18
  ```typescript
14
19
  import { PrivacyCash } from "@velumdotcash/sdk";
15
20
 
16
- // Initialize with wallet signature (browser)
21
+ // Sign a deterministic message to derive shielded keys
22
+ const message = new TextEncoder().encode(
23
+ `Welcome to Velum\n\nSign this message to derive your private encryption keys.\n\nThis request will not trigger a blockchain transaction or cost any fees.\n\nWallet: ${publicKey.toBase58()}`
24
+ );
25
+ const signature = await wallet.signMessage(message);
26
+
17
27
  const sdk = new PrivacyCash({
18
28
  RPC_url: "https://api.mainnet-beta.solana.com",
19
29
  publicKey: walletPublicKey,
20
30
  signature: walletSignature,
21
31
  transactionSigner: async (tx) => wallet.signTransaction(tx),
22
32
  });
33
+ ```
34
+
35
+ ### Node.js (with keypair)
36
+
37
+ ```typescript
38
+ import { Keypair } from "@solana/web3.js";
39
+ import { PrivacyCash } from "@velumdotcash/sdk";
23
40
 
24
- // Or with keypair (Node.js / scripts)
25
41
  const sdk = new PrivacyCash({
26
- RPC_url: "https://api.mainnet-beta.solana.com",
27
- owner: keypair,
42
+ RPC_url: process.env.SOLANA_RPC_URL!,
43
+ owner: Keypair.fromSecretKey(secretKey),
44
+ circuitPath: "./circuits",
28
45
  });
29
46
  ```
30
47
 
31
- ## API Reference
32
-
33
- ### Deposits
48
+ ## Deposits
34
49
 
35
50
  ```typescript
36
- // Deposit SOL
37
- await sdk.deposit({ lamports: 10_000_000 });
51
+ // Deposit SOL to your shielded account
52
+ await sdk.deposit({ lamports: 10_000_000 }); // 0.01 SOL
38
53
 
39
54
  // Deposit USDC
40
- await sdk.depositUSDC({ base_units: 1_000_000 });
55
+ await sdk.depositUSDC({ base_units: 1_000_000 }); // 1 USDC
41
56
 
42
57
  // Deposit any SPL token
43
- await sdk.depositSPL({ base_units: 1_000_000, mintAddress: "..." });
58
+ await sdk.depositSPL({
59
+ base_units: 1_000_000,
60
+ mintAddress: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
61
+ });
44
62
 
45
- // Deposit to a third-party recipient
63
+ // Deposit to a third-party recipient (payment link flow)
46
64
  await sdk.deposit({
47
65
  lamports: 10_000_000,
48
- recipientUtxoPublicKey: "...",
49
- recipientEncryptionKey: encryptionKeyBytes,
66
+ recipientUtxoPublicKey: recipientPubkey,
67
+ recipientEncryptionKey: recipientEncKey,
50
68
  });
51
69
  ```
52
70
 
53
- ### Withdrawals
71
+ ## Withdrawals
54
72
 
55
73
  ```typescript
56
- // Withdraw SOL
57
- await sdk.withdraw({ lamports: 10_000_000, recipientAddress: "..." });
74
+ // Withdraw SOL to any address
75
+ await sdk.withdraw({
76
+ lamports: 10_000_000,
77
+ recipientAddress: "Destination...",
78
+ });
58
79
 
59
80
  // Withdraw USDC
60
- await sdk.withdrawUSDC({ base_units: 1_000_000, recipientAddress: "..." });
81
+ await sdk.withdrawUSDC({
82
+ base_units: 1_000_000,
83
+ recipientAddress: "Destination...",
84
+ });
61
85
 
62
86
  // Withdraw any SPL token
63
87
  await sdk.withdrawSPL({
64
88
  base_units: 1_000_000,
65
- mintAddress: "...",
66
- recipientAddress: "...",
89
+ mintAddress: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
90
+ recipientAddress: "Destination...",
67
91
  });
68
92
  ```
69
93
 
70
- ### Balance
94
+ ## Private Balance
71
95
 
72
96
  ```typescript
73
97
  const sol = await sdk.getPrivateBalance();
74
- console.log(sol.lamports);
75
-
76
98
  const usdc = await sdk.getPrivateBalanceUSDC();
77
- console.log(usdc.base_units);
78
-
79
99
  const spl = await sdk.getPrivateBalanceSpl(mintAddress);
80
- console.log(spl.base_units);
81
100
  ```
82
101
 
83
- ### Key Derivation
102
+ ## Key Derivation
84
103
 
85
104
  ```typescript
86
- // Get public keys for receiving payments
87
- const encryptionKey = sdk.getAsymmetricPublicKey(); // Uint8Array
88
- const utxoPubkey = await sdk.getShieldedPublicKey(); // string
105
+ // Get your shielded public keys (share these to receive payments)
106
+ const encryptionKey = sdk.getAsymmetricPublicKey(); // Uint8Array (X25519)
107
+ const utxoPubkey = await sdk.getShieldedPublicKey(); // string (BN254)
89
108
  ```
90
109
 
91
- ## Browser Usage
110
+ ## How It Works
92
111
 
93
- The SDK works in browsers with wallet adapter integration. Circuit files (WASM + zkey) must be served from your application's public directory.
112
+ 1. **Key derivation**: A wallet signature deterministically derives two keypairs BN254 (UTXO ownership) and X25519 (note encryption)
113
+ 2. **Deposit**: Funds enter a shielded pool via a ZK-SNARK that proves validity without revealing the amount or recipient
114
+ 3. **UTXO creation**: An encrypted note is stored onchain — only the recipient's X25519 key can decrypt it
115
+ 4. **Withdraw**: A second ZK proof verifies UTXO ownership without revealing which deposit created it
94
116
 
95
- ```typescript
96
- const sdk = new PrivacyCash({
97
- RPC_url: rpcEndpoint,
98
- publicKey: walletPublicKey,
99
- signature: walletSignature,
100
- transactionSigner: async (tx) => wallet.signTransaction(tx),
101
- circuitPath: "/circuit", // path to circuit files in public/
102
- });
103
- ```
117
+ The result: no onchain link between sender and receiver.
104
118
 
105
- ## Node.js Usage
119
+ ## Circuit Files
106
120
 
107
- For server-side scripts and testing:
121
+ The SDK requires ZK circuit files (`circuit.wasm` ~3MB, `circuit.zkey` ~16MB) for proof generation.
108
122
 
109
- ```typescript
110
- import { Keypair } from "@solana/web3.js";
123
+ **Browser**: Serve from your public directory. Files are lazy-loaded on first deposit/withdraw and cached in IndexedDB.
111
124
 
112
- const keypair = Keypair.fromSecretKey(secretKey);
125
+ ```typescript
113
126
  const sdk = new PrivacyCash({
114
- RPC_url: process.env.SOLANA_RPC_URL!,
115
- owner: keypair,
116
- storage: new LocalStorage("./cache"),
117
- circuitPath: "./circuits",
118
- enableDebug: true,
127
+ // ...
128
+ circuitPath: "/circuit", // relative to your public dir
119
129
  });
120
130
  ```
121
131
 
122
- ## Error Handling
132
+ **Node.js**: Point to a local directory containing the circuit files.
133
+
134
+ ## Error Types
123
135
 
124
136
  ```typescript
125
137
  import {
@@ -128,15 +140,14 @@ import {
128
140
  NetworkError,
129
141
  TransactionTimeoutError,
130
142
  } from "@velumdotcash/sdk";
131
-
132
- try {
133
- await sdk.deposit({ lamports: amount });
134
- } catch (err) {
135
- if (err instanceof InsufficientBalanceError) { /* ... */ }
136
- if (err instanceof ZKProofError) { /* ... */ }
137
- }
138
143
  ```
139
144
 
145
+ ## Related
146
+
147
+ - [`@velumdotcash/api`](https://www.npmjs.com/package/@velumdotcash/api) — Server-side REST client for paylinks and transactions
148
+ - [Developer Guide](https://velum.cash/docs/developer-guide) — Full integration documentation
149
+ - [How It Works](https://velum.cash/docs/how-it-works) — Cryptographic architecture
150
+
140
151
  ## License
141
152
 
142
- ISC
153
+ [MIT](./LICENSE)
package/dist/deposit.js CHANGED
@@ -8,7 +8,7 @@ import { InsufficientBalanceError, DepositLimitError, TransactionTimeoutError, }
8
8
  import { serializeProofAndExtData, } from "./utils/encryption.js";
9
9
  import { Keypair as UtxoKeypair } from "./models/keypair.js";
10
10
  import { getUtxos } from "./getUtxos.js";
11
- import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS, } from "./utils/constants.js";
11
+ import { FIELD_SIZE, FEE_RECIPIENT, VELUM_FEE_WALLET, VELUM_FEE_BPS, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS, } from "./utils/constants.js";
12
12
  import { useExistingALT } from "./utils/address_lookup_table.js";
13
13
  import { logger } from "./utils/logger.js";
14
14
  // Function to relay pre-signed deposit transaction to indexer backend
@@ -56,15 +56,16 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
56
56
  }
57
57
  // const amount_in_lamports = amount_in_sol * LAMPORTS_PER_SOL
58
58
  const fee_amount_in_lamports = 0;
59
+ const velum_fee_lamports = Math.ceil(amount_in_lamports * VELUM_FEE_BPS / 10_000);
59
60
  logger.debug("Encryption key generated from user keypair");
60
61
  logger.debug(`User wallet: ${signer.toString()}`);
61
62
  logger.debug(`Deposit amount: ${amount_in_lamports} lamports (${amount_in_lamports / LAMPORTS_PER_SOL} SOL)`);
62
- logger.debug(`Calculated fee: ${fee_amount_in_lamports} lamports (${fee_amount_in_lamports / LAMPORTS_PER_SOL} SOL)`);
63
- // Check wallet balance
63
+ logger.debug(`Protocol fee: ${fee_amount_in_lamports} lamports, Velum fee: ${velum_fee_lamports} lamports`);
64
+ // Check wallet balance (deposit + protocol fee + velum fee)
64
65
  const balance = await connection.getBalance(signer);
65
66
  logger.debug(`Wallet balance: ${balance / 1e9} SOL`);
66
- if (balance < amount_in_lamports + fee_amount_in_lamports) {
67
- throw new InsufficientBalanceError(amount_in_lamports + fee_amount_in_lamports, balance);
67
+ if (balance < amount_in_lamports + fee_amount_in_lamports + velum_fee_lamports) {
68
+ throw new InsufficientBalanceError(amount_in_lamports + fee_amount_in_lamports + velum_fee_lamports, balance);
68
69
  }
69
70
  const { treeAccount, treeTokenAccount, globalConfigAccount } = getProgramAccounts();
70
71
  // Create the merkle tree with the pre-initialized poseidon hash
@@ -364,12 +365,18 @@ export async function deposit({ lightWasm, storage, keyBasePath, publicKey, conn
364
365
  const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
365
366
  units: 1_000_000,
366
367
  });
368
+ // Velum fee transfer instruction
369
+ const velumFeeInstruction = SystemProgram.transfer({
370
+ fromPubkey: signer,
371
+ toPubkey: VELUM_FEE_WALLET,
372
+ lamports: velum_fee_lamports,
373
+ });
367
374
  // Create versioned transaction with Address Lookup Table
368
375
  const recentBlockhash = await connection.getLatestBlockhash();
369
376
  const messageV0 = new TransactionMessage({
370
377
  payerKey: signer, // User pays for their own deposit
371
378
  recentBlockhash: recentBlockhash.blockhash,
372
- instructions: [modifyComputeUnits, depositInstruction],
379
+ instructions: [modifyComputeUnits, velumFeeInstruction, depositInstruction],
373
380
  }).compileToV0Message([lookupTableAccount.value]);
374
381
  let versionedTransaction = new VersionedTransaction(messageV0);
375
382
  // sign tx
@@ -7,10 +7,10 @@ import { MerkleTree } from "./utils/merkle_tree.js";
7
7
  import { serializeProofAndExtData, } from "./utils/encryption.js";
8
8
  import { Keypair as UtxoKeypair } from "./models/keypair.js";
9
9
  import { getUtxosSPL } from "./getUtxosSPL.js";
10
- import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS, tokens, } from "./utils/constants.js";
10
+ import { FIELD_SIZE, FEE_RECIPIENT, MERKLE_TREE_DEPTH, RELAYER_API_URL, PROGRAM_ID, ALT_ADDRESS, tokens, VELUM_FEE_WALLET, VELUM_FEE_BPS, } from "./utils/constants.js";
11
11
  import { useExistingALT, } from "./utils/address_lookup_table.js";
12
12
  import { logger } from "./utils/logger.js";
13
- import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, getAccount, } from "@solana/spl-token";
13
+ import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, getAccount, createTransferInstruction, } from "@solana/spl-token";
14
14
  // Function to relay pre-signed deposit transaction to indexer backend
15
15
  async function relayDepositToIndexer({ signedTransaction, publicKey, referrer, mintAddress, }) {
16
16
  try {
@@ -84,6 +84,7 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
84
84
  }
85
85
  // const base_units = amount_in_sol * units_per_token
86
86
  const fee_base_units = 0;
87
+ const velum_fee_base_units = Math.ceil(base_units * VELUM_FEE_BPS / 10_000);
87
88
  logger.debug("Encryption key generated from user keypair");
88
89
  logger.debug(`User wallet: ${signer.toString()}`);
89
90
  logger.debug(`Deposit amount: ${base_units} base_units (${base_units / token.units_per_token} ${token.name.toUpperCase()})`);
@@ -94,8 +95,8 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
94
95
  logger.debug(`wallet balance: ${balance / token.units_per_token} ${token.name.toUpperCase()}`);
95
96
  logger.debug("balance", balance);
96
97
  logger.debug("base_units + fee_base_units", base_units + fee_base_units);
97
- if (balance < base_units + fee_base_units) {
98
- throw new Error(`Insufficient balance. Need at least ${(base_units + fee_base_units) / token.units_per_token} ${token.name.toUpperCase()}.`);
98
+ if (balance < base_units + fee_base_units + velum_fee_base_units) {
99
+ throw new Error(`Insufficient balance. Need at least ${(base_units + fee_base_units + velum_fee_base_units) / token.units_per_token} ${token.name.toUpperCase()}.`);
99
100
  }
100
101
  // Check SOL balance for account rent + transaction fees
101
102
  // The program creates a rent-exempt account (~953,520 lamports) during deposit
@@ -412,12 +413,15 @@ export async function depositSPL({ lightWasm, storage, keyBasePath, publicKey, c
412
413
  const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
413
414
  units: 1_000_000,
414
415
  });
416
+ // Velum fee transfer instruction (SPL token)
417
+ const velumFeeATA = getAssociatedTokenAddressSync(token.pubkey, VELUM_FEE_WALLET);
418
+ const velumFeeInstruction = createTransferInstruction(signerTokenAccount, velumFeeATA, signer, velum_fee_base_units);
415
419
  // Create versioned transaction with Address Lookup Table
416
420
  const recentBlockhash = await connection.getLatestBlockhash();
417
421
  const messageV0 = new TransactionMessage({
418
422
  payerKey: signer, // User pays for their own deposit
419
423
  recentBlockhash: recentBlockhash.blockhash,
420
- instructions: [modifyComputeUnits, depositInstruction],
424
+ instructions: [modifyComputeUnits, velumFeeInstruction, depositInstruction],
421
425
  }).compileToV0Message([lookupTableAccount.value]);
422
426
  let versionedTransaction = new VersionedTransaction(messageV0);
423
427
  // sign tx
@@ -3,6 +3,8 @@ import BN from 'bn.js';
3
3
  export declare const FIELD_SIZE: BN;
4
4
  export declare const PROGRAM_ID: PublicKey;
5
5
  export declare const FEE_RECIPIENT: PublicKey;
6
+ export declare const VELUM_FEE_WALLET: PublicKey;
7
+ export declare const VELUM_FEE_BPS = 15;
6
8
  export declare const FETCH_UTXOS_GROUP_SIZE = 20000;
7
9
  export declare const TRANSACT_IX_DISCRIMINATOR: Buffer<ArrayBuffer>;
8
10
  export declare const TRANSACT_SPL_IX_DISCRIMINATOR: Buffer<ArrayBuffer>;
@@ -3,6 +3,8 @@ import BN from 'bn.js';
3
3
  export const FIELD_SIZE = new BN('21888242871839275222246405745257275088548364400416034343698204186575808495617');
4
4
  export const PROGRAM_ID = process.env.NEXT_PUBLIC_PROGRAM_ID ? new PublicKey(process.env.NEXT_PUBLIC_PROGRAM_ID) : new PublicKey('9fhQBbumKEFuXtMBDw8AaQyAjCorLGJQiS3skWZdQyQD');
5
5
  export const FEE_RECIPIENT = new PublicKey('AWexibGxNFKTa1b5R5MN4PJr9HWnWRwf8EW9g8cLx3dM');
6
+ export const VELUM_FEE_WALLET = new PublicKey('8CzjWm8yJBMZhBNwQtRMAgvf7xf8TFQMwQGuv4pd4A4s');
7
+ export const VELUM_FEE_BPS = 15; // 0.15% = 15 basis points
6
8
  export const FETCH_UTXOS_GROUP_SIZE = 20_000;
7
9
  export const TRANSACT_IX_DISCRIMINATOR = Buffer.from([217, 149, 130, 143, 221, 52, 252, 119]);
8
10
  export const TRANSACT_SPL_IX_DISCRIMINATOR = Buffer.from([154, 66, 244, 204, 78, 225, 163, 151]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velumdotcash/sdk",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "TypeScript SDK for private payments on Solana using Zero-Knowledge proofs",
5
5
  "main": "dist/index.js",
6
6
  "exports": {