@veil-cash/sdk 0.6.2 → 0.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veil-cash/sdk",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "SDK and CLI for interacting with Veil Cash privacy pools - keypair generation, deposits, and status checking",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -165,7 +165,7 @@ What do you want to do?
165
165
  |
166
166
  +-- Register deposit key on-chain → veil register [--unsigned]
167
167
  |
168
- +-- Deposit ETH or USDC → veil deposit <asset> <amount> [--unsigned]
168
+ +-- Deposit ETH or USDC → veil deposit <asset> <amount> [--unsigned --address 0x...]
169
169
  |
170
170
  +-- Check balances → veil balance [queue|private] [--pool eth|usdc]
171
171
  |
@@ -330,7 +330,9 @@ Important:
330
330
  - If not yet registered, returns a normal `register` payload.
331
331
 
332
332
  Deposits treat the CLI amount as the **net** amount that lands in the pool.
333
- The `0.3%` protocol fee is calculated on-chain and added automatically.
333
+ Each address gets a configurable number of fee-free deposits per UTC day.
334
+ The CLI checks automatically — if free slots remain the fee is waived;
335
+ otherwise the `0.3%` protocol fee is calculated on-chain and added.
334
336
  After submission, deposits go through screening / queue processing before they
335
337
  are accepted into the private pool. This typically takes around `10-15 minutes`.
336
338
 
@@ -338,8 +340,8 @@ are accepted into the private pool. This typically takes around `10-15 minutes`.
338
340
  veil deposit ETH 0.1
339
341
  veil deposit USDC 100
340
342
  veil deposit ETH 0.1 --json
341
- veil deposit ETH 0.1 --unsigned
342
- veil deposit USDC 100 --unsigned
343
+ veil deposit ETH 0.1 --unsigned --address 0x...
344
+ SIGNER_ADDRESS=0x... veil deposit USDC 100 --unsigned
343
345
  ```
344
346
 
345
347
  Minimums:
@@ -258,8 +258,8 @@ SIGNER_ADDRESS=0x... veil register --unsigned --force # Unsigned register/change
258
258
  veil register --unsigned --address 0x... # Unsigned register payload (explicit address)
259
259
  veil register --json # Register and output result as JSON
260
260
 
261
- veil deposit ETH 0.1 --unsigned # Unsigned ETH deposit payload
262
- veil deposit USDC 100 --unsigned # Unsigned USDC deposit payload(s)
261
+ veil deposit ETH 0.1 --unsigned --address 0x... # Unsigned ETH deposit payload
262
+ SIGNER_ADDRESS=0x... veil deposit USDC 100 --unsigned # Unsigned USDC deposit payload(s)
263
263
  veil deposit ETH 0.1 --json # Deposit and output result as JSON
264
264
 
265
265
  veil balance # All pool balances
@@ -307,10 +307,10 @@ Common codes: `VEIL_KEY_MISSING`, `WALLET_KEY_MISSING`, `DEPOSIT_KEY_MISSING`,
307
307
 
308
308
  | Asset | Minimum (net) | Notes |
309
309
  |-------|--------------|-------|
310
- | ETH | 0.01 | Fee (0.3%) added automatically via on-chain `getDepositAmountWithFee` |
311
- | USDC | 10 | Fee (0.3%) added automatically via on-chain `getDepositAmountWithFee` |
310
+ | ETH | 0.01 | Fee (0.3%) added automatically, or waived if daily free deposits remain |
311
+ | USDC | 10 | Fee (0.3%) added automatically, or waived if daily free deposits remain |
312
312
 
313
- The CLI amount is the **net** amount that lands in the pool. The fee is calculated on-chain and added to the transaction automatically — users do not need to account for it.
313
+ The CLI amount is the **net** amount that lands in the pool. The CLI checks `getDailyFreeRemaining` on the queue contract — if the user has free slots left today the fee is skipped; otherwise the 0.3% fee is calculated on-chain and added to the transaction automatically.
314
314
 
315
315
  ---
316
316
 
package/src/abi.ts CHANGED
@@ -218,6 +218,14 @@ export const QUEUE_ABI = [
218
218
  stateMutability: 'view',
219
219
  type: 'function',
220
220
  },
221
+ // Get remaining daily free deposits for an address (V3+)
222
+ {
223
+ inputs: [{ name: '_depositor', type: 'address' }],
224
+ name: 'getDailyFreeRemaining',
225
+ outputs: [{ name: 'remaining', type: 'uint256' }],
226
+ stateMutability: 'view',
227
+ type: 'function',
228
+ },
221
229
  ] as const;
222
230
 
223
231
  /**
package/src/balance.ts CHANGED
@@ -127,6 +127,54 @@ export async function getQueueBalance(options: {
127
127
  };
128
128
  }
129
129
 
130
+ /**
131
+ * Get remaining daily free deposits for an address.
132
+ * Returns 0 if the queue contract has not been upgraded to V3 yet
133
+ * or if the daily free feature is disabled.
134
+ *
135
+ * @param options - Query options
136
+ * @param options.address - Depositor address to check
137
+ * @param options.pool - Pool identifier ('eth' or 'usdc', default: 'eth')
138
+ * @param options.rpcUrl - Optional RPC URL
139
+ * @returns Number of free deposits remaining today
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const remaining = await getDailyFreeRemaining({
144
+ * address: '0x...',
145
+ * pool: 'eth',
146
+ * });
147
+ * console.log(`Free deposits left today: ${remaining}`);
148
+ * ```
149
+ */
150
+ export async function getDailyFreeRemaining(options: {
151
+ address: `0x${string}`;
152
+ pool?: RelayPool;
153
+ rpcUrl?: string;
154
+ }): Promise<number> {
155
+ const { address, pool = 'eth', rpcUrl } = options;
156
+ const queueAddress = getQueueAddress(pool);
157
+
158
+ const publicClient = createPublicClient({
159
+ chain: base,
160
+ transport: http(rpcUrl),
161
+ });
162
+
163
+ try {
164
+ const remaining = await publicClient.readContract({
165
+ address: queueAddress,
166
+ abi: QUEUE_ABI,
167
+ functionName: 'getDailyFreeRemaining',
168
+ args: [address],
169
+ }) as bigint;
170
+
171
+ return Number(remaining);
172
+ } catch {
173
+ // V2 contracts don't have this function — treat as 0 remaining
174
+ return 0;
175
+ }
176
+ }
177
+
130
178
  /**
131
179
  * Get private balance from the Pool contract
132
180
  * Decrypts all encrypted outputs, calculates nullifiers, and checks spent status
@@ -4,8 +4,9 @@
4
4
 
5
5
  import { Command } from 'commander';
6
6
  import { buildDepositETHTx, buildDepositUSDCTx, buildApproveUSDCTx } from '../../deposit.js';
7
+ import { getDailyFreeRemaining } from '../../balance.js';
7
8
  import { sendTransaction, getAddress, getBalance } from '../wallet.js';
8
- import { getConfig } from '../config.js';
9
+ import { getConfig, resolveAddress } from '../config.js';
9
10
  import { createPublicClient, http, parseEther, parseUnits, formatEther, formatUnits } from 'viem';
10
11
  import { base } from 'viem/chains';
11
12
  import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
@@ -13,6 +14,7 @@ import { clearProgress, createProgressReporter, printFields, printHeader, printJ
13
14
  import { POOL_CONFIG, getAddresses } from '../../addresses.js';
14
15
  import { ENTRY_ABI, ERC20_ABI } from '../../abi.js';
15
16
  import type { TransactionData } from '../../types.js';
17
+ import type { WalletConfig } from '../wallet.js';
16
18
 
17
19
  const MINIMUM_NET: Record<string, number> = {
18
20
  ETH: 0.01,
@@ -20,13 +22,22 @@ const MINIMUM_NET: Record<string, number> = {
20
22
  };
21
23
 
22
24
  /**
23
- * Query the Entry contract for the exact gross amount (net + fee).
24
- * This matches the contract's own fee math and avoids rounding mismatches.
25
+ * Compute the gross amount and fee for a deposit.
26
+ * Checks daily free deposit availability first if the user has
27
+ * free slots remaining the fee is waived and gross === net.
25
28
  */
26
29
  async function getGrossAmount(
27
30
  netWei: bigint,
31
+ depositor: `0x${string}`,
32
+ pool: 'eth' | 'usdc',
28
33
  rpcUrl: string | undefined,
29
- ): Promise<{ grossWei: bigint; feeWei: bigint }> {
34
+ ): Promise<{ grossWei: bigint; feeWei: bigint; dailyFreeUsed: boolean; dailyFreeRemaining: number }> {
35
+ const freeRemaining = await getDailyFreeRemaining({ address: depositor, pool, rpcUrl });
36
+
37
+ if (freeRemaining > 0) {
38
+ return { grossWei: netWei, feeWei: 0n, dailyFreeUsed: true, dailyFreeRemaining: freeRemaining - 1 };
39
+ }
40
+
30
41
  const publicClient = createPublicClient({
31
42
  chain: base,
32
43
  transport: http(rpcUrl),
@@ -39,7 +50,7 @@ async function getGrossAmount(
39
50
  args: [netWei],
40
51
  }) as bigint;
41
52
 
42
- return { grossWei, feeWei: grossWei - netWei };
53
+ return { grossWei, feeWei: grossWei - netWei, dailyFreeUsed: false, dailyFreeRemaining: 0 };
43
54
  }
44
55
 
45
56
  const SUPPORTED_ASSETS = ['ETH', 'USDC'];
@@ -49,16 +60,19 @@ export function createDepositCommand(): Command {
49
60
  .description('Deposit ETH or USDC into Veil')
50
61
  .argument('<asset>', 'Asset to deposit (ETH or USDC)')
51
62
  .argument('<amount>', 'Amount to deposit — this is what arrives in your Veil balance')
63
+ .option('--address <address>', 'Signer address (required in --unsigned mode unless SIGNER_ADDRESS or WALLET_KEY is set)')
52
64
  .option('--unsigned', 'Output unsigned transaction payload instead of sending')
53
65
  .option('--json', 'Output as JSON')
54
66
  .addHelpText('after', `
55
67
  The amount you specify is the net amount that lands in your Veil balance.
56
- The 0.3% protocol fee is automatically added on top.
68
+ A 0.3% protocol fee is normally added on top, but each address gets
69
+ free daily deposits (fee waived). The CLI checks automatically.
57
70
 
58
71
  Examples:
59
- veil deposit ETH 0.1 # deposits 0.1 ETH (sends ~0.1003 ETH)
60
- veil deposit USDC 100 # deposits 100 USDC (sends ~100.30 USDC)
61
- veil deposit ETH 0.1 --unsigned
72
+ veil deposit ETH 0.1 # deposits 0.1 ETH (free or ~0.1003 ETH)
73
+ veil deposit USDC 100 # deposits 100 USDC (free or ~100.30 USDC)
74
+ veil deposit ETH 0.1 --unsigned --address 0x...
75
+ SIGNER_ADDRESS=0x... veil deposit ETH 0.1 --unsigned
62
76
  veil deposit ETH 0.1 --json
63
77
  `)
64
78
  .action(async (asset: string, amount: string, options) => {
@@ -80,15 +94,41 @@ Examples:
80
94
  }
81
95
 
82
96
  const rpcUrl = process.env.RPC_URL;
83
- const poolConfig = POOL_CONFIG[assetUpper.toLowerCase() as 'eth' | 'usdc'];
97
+ const pool = assetUpper.toLowerCase() as 'eth' | 'usdc';
98
+ const poolConfig = POOL_CONFIG[pool];
84
99
  const netWei = assetUpper === 'ETH'
85
100
  ? parseEther(amount)
86
101
  : parseUnits(amount, poolConfig.decimals);
87
102
 
88
103
  const progress = createProgressReporter();
89
- progress('Calculating fee...');
90
104
 
91
- const { grossWei, feeWei } = await getGrossAmount(netWei, rpcUrl);
105
+ let config: WalletConfig | null = null;
106
+ let address: `0x${string}`;
107
+ let feeRpcUrl = rpcUrl;
108
+
109
+ if (options.unsigned) {
110
+ const resolved = resolveAddress({ address: options.address }, { required: true });
111
+ if (!resolved) {
112
+ throw new CLIError(
113
+ ErrorCode.WALLET_KEY_MISSING,
114
+ 'Must provide --address, set SIGNER_ADDRESS, or set WALLET_KEY env.',
115
+ );
116
+ }
117
+ address = resolved.address;
118
+ } else {
119
+ config = getConfig(options);
120
+ address = getAddress(config.privateKey);
121
+ feeRpcUrl = config.rpcUrl;
122
+ }
123
+
124
+ progress('Checking deposit fee...');
125
+
126
+ const { grossWei, feeWei, dailyFreeUsed, dailyFreeRemaining } = await getGrossAmount(
127
+ netWei,
128
+ address,
129
+ pool,
130
+ feeRpcUrl,
131
+ );
92
132
  const grossStr = assetUpper === 'ETH'
93
133
  ? formatEther(grossWei)
94
134
  : formatUnits(grossWei, poolConfig.decimals);
@@ -140,8 +180,9 @@ Examples:
140
180
  return;
141
181
  }
142
182
 
143
- const config = getConfig(options);
144
- const address = getAddress(config.privateKey);
183
+ if (!config) {
184
+ throw new CLIError(ErrorCode.WALLET_KEY_MISSING, 'WALLET_KEY env var required. Set it before running this command.');
185
+ }
145
186
 
146
187
  if (assetUpper === 'ETH') {
147
188
  progress('Checking balance...');
@@ -203,6 +244,7 @@ Examples:
203
244
  asset: assetUpper,
204
245
  amount,
205
246
  fee: feeStr,
247
+ dailyFreeUsed,
206
248
  totalSent: grossStr,
207
249
  blockNumber: result.receipt.blockNumber.toString(),
208
250
  };
@@ -212,11 +254,15 @@ Examples:
212
254
  return;
213
255
  }
214
256
 
257
+ const feeLabel = dailyFreeUsed
258
+ ? `0 ${assetUpper} (free — ${dailyFreeRemaining} remaining today)`
259
+ : `${feeStr} ${assetUpper} (0.3%)`;
260
+
215
261
  printHeader('Deposit Submitted');
216
262
  printFields([
217
263
  { label: 'Asset', value: assetUpper },
218
264
  { label: 'Amount', value: `${amount} ${assetUpper}` },
219
- { label: 'Fee', value: `${feeStr} ${assetUpper} (0.3%)` },
265
+ { label: 'Fee', value: feeLabel },
220
266
  { label: 'Total sent', value: `${grossStr} ${assetUpper}` },
221
267
  { label: 'From', value: address },
222
268
  { label: 'Transaction', value: txUrl(result.hash) },
package/src/index.ts CHANGED
@@ -53,6 +53,7 @@ export {
53
53
  export {
54
54
  getQueueBalance,
55
55
  getPrivateBalance,
56
+ getDailyFreeRemaining,
56
57
  } from './balance.js';
57
58
  export type { ProgressCallback } from './balance.js';
58
59