@veil-cash/sdk 0.1.1 → 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
@@ -6,7 +6,7 @@
6
6
 
7
7
  SDK and CLI for interacting with [Veil Cash](https://veil.cash) privacy pools on Base.
8
8
 
9
- Generate keypairs, register, deposit, withdraw, and transfer ETH privately.
9
+ Generate keypairs, register, deposit, withdraw, transfer, and merge ETH privately.
10
10
 
11
11
  ## Installation
12
12
 
@@ -35,17 +35,23 @@ export WALLET_KEY=0x...
35
35
  # 3. Register your deposit key (one-time)
36
36
  veil register
37
37
 
38
- # 4. Deposit ETH
39
- veil deposit 0.1
38
+ # 4. Check your setup
39
+ veil status
40
40
 
41
- # 5. Check your balance
41
+ # 5. Deposit ETH
42
+ veil deposit ETH 0.1
43
+
44
+ # 6. Check your balance
42
45
  veil balance
43
46
 
44
- # 6. Withdraw to any address
45
- veil withdraw 0.05 0xRecipientAddress
47
+ # 7. Withdraw to any address
48
+ veil withdraw ETH 0.05 0xRecipientAddress
49
+
50
+ # 8. Transfer privately to another registered user
51
+ veil transfer ETH 0.02 0xRecipientAddress
46
52
 
47
- # 7. Transfer privately to another registered user
48
- veil transfer 0.02 0xRecipientAddress
53
+ # 9. Merge small UTXOs (consolidate balances)
54
+ veil merge ETH 0.1
49
55
  ```
50
56
 
51
57
  ## CLI Commands
@@ -64,13 +70,43 @@ veil init --no-save # Print keypair without saving
64
70
 
65
71
  ### `veil keypair`
66
72
 
67
- Generate and show a new keypair as JSON (does not save).
73
+ Show current Veil keypair as JSON (from VEIL_KEY env).
68
74
 
69
75
  ```bash
70
76
  veil keypair
71
77
  # {"veilPrivateKey":"0x...","depositKey":"0x..."}
72
78
  ```
73
79
 
80
+ ### `veil status`
81
+
82
+ Check configuration and service status.
83
+
84
+ ```bash
85
+ veil status
86
+ ```
87
+
88
+ Output:
89
+ ```json
90
+ {
91
+ "walletKey": { "found": true, "address": "0x..." },
92
+ "veilKey": { "found": true },
93
+ "depositKey": { "found": true, "key": "0x1234...abcd" },
94
+ "rpcUrl": { "found": false, "url": "https://mainnet.base.org" },
95
+ "registration": {
96
+ "checked": true,
97
+ "registered": true,
98
+ "matches": true,
99
+ "onChainKey": "0x..."
100
+ },
101
+ "relay": {
102
+ "checked": true,
103
+ "healthy": true,
104
+ "status": "ok",
105
+ "network": "mainnet"
106
+ }
107
+ }
108
+ ```
109
+
74
110
  ### `veil register`
75
111
 
76
112
  Register your deposit key on-chain (one-time per address).
@@ -81,14 +117,14 @@ veil register --json # JSON output
81
117
  veil register --unsigned --address 0x... # Unsigned payload for agents
82
118
  ```
83
119
 
84
- ### `veil deposit <amount>`
120
+ ### `veil deposit ETH <amount>`
85
121
 
86
122
  Deposit ETH into the privacy pool.
87
123
 
88
124
  ```bash
89
- veil deposit 0.1 # Signs & sends (JSON output)
90
- veil deposit 0.1 --unsigned # Unsigned payload for agents
91
- veil deposit 0.1 --quiet # Suppress progress output
125
+ veil deposit ETH 0.1 # Signs & sends (JSON output)
126
+ veil deposit ETH 0.1 --unsigned # Unsigned payload for agents
127
+ veil deposit ETH 0.1 --quiet # Suppress progress output
92
128
  ```
93
129
 
94
130
  Output:
@@ -138,13 +174,13 @@ Output:
138
174
  }
139
175
  ```
140
176
 
141
- ### `veil withdraw <amount> <recipient>`
177
+ ### `veil withdraw ETH <amount> <recipient>`
142
178
 
143
179
  Withdraw from the privacy pool to any public address.
144
180
 
145
181
  ```bash
146
- veil withdraw 0.05 0xRecipientAddress
147
- veil withdraw 0.05 0xRecipientAddress --quiet
182
+ veil withdraw ETH 0.05 0xRecipientAddress
183
+ veil withdraw ETH 0.05 0xRecipientAddress --quiet
148
184
  ```
149
185
 
150
186
  Output:
@@ -159,13 +195,13 @@ Output:
159
195
  }
160
196
  ```
161
197
 
162
- ### `veil transfer <amount> <recipient>`
198
+ ### `veil transfer ETH <amount> <recipient>`
163
199
 
164
200
  Transfer privately to another registered Veil user.
165
201
 
166
202
  ```bash
167
- veil transfer 0.02 0xRecipientAddress
168
- veil transfer 0.02 0xRecipientAddress --quiet
203
+ veil transfer ETH 0.02 0xRecipientAddress
204
+ veil transfer ETH 0.02 0xRecipientAddress --quiet
169
205
  ```
170
206
 
171
207
  Output:
@@ -180,13 +216,24 @@ Output:
180
216
  }
181
217
  ```
182
218
 
183
- ### `veil merge <amount>`
219
+ ### `veil merge ETH <amount>`
184
220
 
185
221
  Consolidate multiple small UTXOs into one (self-transfer).
186
222
 
187
223
  ```bash
188
- veil merge 0.1 # Merge UTXOs totaling 0.1 ETH
189
- veil merge 0.1 --quiet
224
+ veil merge ETH 0.1 # Merge UTXOs totaling 0.1 ETH
225
+ veil merge ETH 0.1 --quiet
226
+ ```
227
+
228
+ Output:
229
+ ```json
230
+ {
231
+ "success": true,
232
+ "transactionHash": "0x...",
233
+ "blockNumber": 12345678,
234
+ "amount": "0.1",
235
+ "type": "merge"
236
+ }
190
237
  ```
191
238
 
192
239
  ## Environment Variables
@@ -391,11 +438,11 @@ veil init --json
391
438
 
392
439
  # Get unsigned transaction payloads for agent signing
393
440
  veil register --unsigned --address 0x...
394
- veil deposit 0.1 --unsigned
441
+ veil deposit ETH 0.1 --unsigned
395
442
 
396
443
  # Suppress progress output for clean JSON
397
444
  veil balance --quiet
398
- veil withdraw 0.05 0xRecipient --quiet
445
+ veil withdraw ETH 0.05 0xRecipient --quiet
399
446
  ```
400
447
 
401
448
  ### Bankr Integration
@@ -403,7 +450,7 @@ veil withdraw 0.05 0xRecipient --quiet
403
450
  Use `--unsigned` to get Bankr-compatible transaction payloads:
404
451
 
405
452
  ```bash
406
- veil deposit 0.1 --unsigned
453
+ veil deposit ETH 0.1 --unsigned
407
454
  # {"to":"0x...","data":"0x...","value":"100000000000000000","chainId":8453}
408
455
  ```
409
456
 
@@ -435,14 +482,15 @@ const result = await withdraw({
435
482
 
436
483
  1. **Generate Keypair**: Run `veil init` to create and save your Veil keypair
437
484
  2. **Register**: Run `veil register` to link your deposit key on-chain (one-time)
438
- 3. **Deposit**: Run `veil deposit <amount>` to send ETH
439
- 4. **Wait**: The Veil deposit engine processes your deposit
440
- 5. **Done**: Your deposit is accepted into the privacy pool
485
+ 3. **Check Status**: Run `veil status` to verify your setup
486
+ 4. **Deposit**: Run `veil deposit ETH <amount>` to send ETH
487
+ 5. **Wait**: The Veil deposit engine processes your deposit
488
+ 6. **Done**: Your deposit is accepted into the privacy pool
441
489
 
442
490
  ## Withdrawal Flow
443
491
 
444
492
  1. **Check Balance**: Run `veil balance` to see your private balance
445
- 2. **Withdraw**: Run `veil withdraw <amount> <recipient>`
493
+ 2. **Withdraw**: Run `veil withdraw ETH <amount> <recipient>`
446
494
  3. **Done**: The SDK builds ZK proofs and submits via the relayer
447
495
 
448
496
  ## License
@@ -5170,8 +5170,11 @@ function progress(msg, quiet) {
5170
5170
  }
5171
5171
  }
5172
5172
  function createDepositCommand() {
5173
- const deposit = new Command("deposit").description("Deposit ETH into Veil").argument("<amount>", "Amount to deposit (e.g., 0.1)").option("--deposit-key <key>", "Your Veil deposit key (or set DEPOSIT_KEY env)").option("--wallet-key <key>", "Ethereum wallet key for signing (or set WALLET_KEY env)").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").option("--unsigned", "Output unsigned transaction payload (Bankr-compatible format)").option("--quiet", "Suppress progress output").action(async (amount, options) => {
5173
+ const deposit = new Command("deposit").description("Deposit ETH into Veil").argument("<asset>", "Asset to deposit (ETH)").argument("<amount>", "Amount to deposit (e.g., 0.1)").option("--deposit-key <key>", "Your Veil deposit key (or set DEPOSIT_KEY env)").option("--wallet-key <key>", "Ethereum wallet key for signing (or set WALLET_KEY env)").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").option("--unsigned", "Output unsigned transaction payload (Bankr-compatible format)").option("--quiet", "Suppress progress output").action(async (asset, amount, options) => {
5174
5174
  try {
5175
+ if (asset.toUpperCase() !== "ETH") {
5176
+ throw new CLIError(ErrorCode.INVALID_AMOUNT, "Only ETH is supported");
5177
+ }
5175
5178
  const amountNum = parseFloat(amount);
5176
5179
  if (amountNum < MINIMUM_DEPOSIT_WITH_FEE) {
5177
5180
  throw new CLIError(
@@ -5847,6 +5850,14 @@ async function submitRelay(options) {
5847
5850
  }
5848
5851
  return data;
5849
5852
  }
5853
+ async function checkRelayHealth(relayUrl) {
5854
+ const url = getRelayUrl();
5855
+ const response = await fetch(`${url}/health`);
5856
+ if (!response.ok) {
5857
+ throw new RelayError("Relay service health check failed", response.status);
5858
+ }
5859
+ return response.json();
5860
+ }
5850
5861
 
5851
5862
  // src/withdraw.ts
5852
5863
  function selectUtxosForWithdraw(utxos, amount, decimals = 18) {
@@ -6018,8 +6029,11 @@ function progress2(msg, quiet) {
6018
6029
  }
6019
6030
  }
6020
6031
  function createWithdrawCommand() {
6021
- const withdrawCmd = new Command("withdraw").description("Withdraw from private pool to a public address").argument("<amount>", "Amount to withdraw (e.g., 0.1)").argument("<recipient>", "Recipient address (e.g., 0x...)").option("--veil-key <key>", "Veil private key (or set VEIL_KEY env)").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").option("--quiet", "Suppress progress output").action(async (amount, recipient, options) => {
6032
+ const withdrawCmd = new Command("withdraw").description("Withdraw from private pool to a public address").argument("<asset>", "Asset to withdraw (ETH)").argument("<amount>", "Amount to withdraw (e.g., 0.1)").argument("<recipient>", "Recipient address (e.g., 0x...)").option("--veil-key <key>", "Veil private key (or set VEIL_KEY env)").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").option("--quiet", "Suppress progress output").action(async (asset, amount, recipient, options) => {
6022
6033
  try {
6034
+ if (asset.toUpperCase() !== "ETH") {
6035
+ throw new CLIError(ErrorCode.INVALID_AMOUNT, "Only ETH is supported");
6036
+ }
6023
6037
  if (!/^0x[a-fA-F0-9]{40}$/.test(recipient)) {
6024
6038
  throw new CLIError(ErrorCode.INVALID_ADDRESS, "Invalid recipient address format");
6025
6039
  }
@@ -6337,8 +6351,11 @@ function progress3(msg, quiet) {
6337
6351
  }
6338
6352
  }
6339
6353
  function createTransferCommand() {
6340
- const transferCmd = new Command("transfer").description("Transfer privately within the pool to another registered address").argument("<amount>", "Amount to transfer (e.g., 0.1)").argument("<recipient>", "Recipient address (must be registered)").option("--veil-key <key>", "Veil private key (or set VEIL_KEY env)").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").option("--quiet", "Suppress progress output").action(async (amount, recipient, options) => {
6354
+ const transferCmd = new Command("transfer").description("Transfer privately within the pool to another registered address").argument("<asset>", "Asset to transfer (ETH)").argument("<amount>", "Amount to transfer (e.g., 0.1)").argument("<recipient>", "Recipient address (must be registered)").option("--veil-key <key>", "Veil private key (or set VEIL_KEY env)").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").option("--quiet", "Suppress progress output").action(async (asset, amount, recipient, options) => {
6341
6355
  try {
6356
+ if (asset.toUpperCase() !== "ETH") {
6357
+ throw new CLIError(ErrorCode.INVALID_AMOUNT, "Only ETH is supported");
6358
+ }
6342
6359
  if (!/^0x[a-fA-F0-9]{40}$/.test(recipient)) {
6343
6360
  throw new CLIError(ErrorCode.INVALID_ADDRESS, "Invalid recipient address format");
6344
6361
  }
@@ -6378,8 +6395,11 @@ function createTransferCommand() {
6378
6395
  return transferCmd;
6379
6396
  }
6380
6397
  function createMergeCommand() {
6381
- const mergeCmd = new Command("merge").description("Merge UTXOs by self-transfer (consolidate small UTXOs)").argument("<amount>", "Amount to merge (e.g., 0.5)").option("--veil-key <key>", "Veil private key (or set VEIL_KEY env)").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").option("--quiet", "Suppress progress output").action(async (amount, options) => {
6398
+ const mergeCmd = new Command("merge").description("Merge UTXOs by self-transfer (consolidate small UTXOs)").argument("<asset>", "Asset to merge (ETH)").argument("<amount>", "Amount to merge (e.g., 0.5)").option("--veil-key <key>", "Veil private key (or set VEIL_KEY env)").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").option("--quiet", "Suppress progress output").action(async (asset, amount, options) => {
6382
6399
  try {
6400
+ if (asset.toUpperCase() !== "ETH") {
6401
+ throw new CLIError(ErrorCode.INVALID_AMOUNT, "Only ETH is supported");
6402
+ }
6383
6403
  const veilKey = options.veilKey || process.env.VEIL_KEY;
6384
6404
  if (!veilKey) {
6385
6405
  throw new CLIError(ErrorCode.VEIL_KEY_MISSING, "VEIL_KEY required. Use --veil-key or set VEIL_KEY env");
@@ -6414,6 +6434,75 @@ function createMergeCommand() {
6414
6434
  return mergeCmd;
6415
6435
  }
6416
6436
 
6437
+ // src/cli/commands/status.ts
6438
+ function createStatusCommand() {
6439
+ const status = new Command("status").description("Check configuration and service status").option("--rpc-url <url>", "RPC URL (or set RPC_URL env)").action(async (options) => {
6440
+ const result = {
6441
+ walletKey: { found: false },
6442
+ veilKey: { found: false },
6443
+ depositKey: { found: false },
6444
+ rpcUrl: { found: false, url: "https://mainnet.base.org" },
6445
+ registration: { checked: false },
6446
+ relay: { checked: false }
6447
+ };
6448
+ const walletKey = process.env.WALLET_KEY;
6449
+ if (walletKey) {
6450
+ result.walletKey.found = true;
6451
+ try {
6452
+ result.walletKey.address = getAddress(walletKey);
6453
+ } catch {
6454
+ }
6455
+ }
6456
+ const veilKey = process.env.VEIL_KEY;
6457
+ if (veilKey) {
6458
+ result.veilKey.found = true;
6459
+ }
6460
+ const depositKey = process.env.DEPOSIT_KEY;
6461
+ if (depositKey) {
6462
+ result.depositKey.found = true;
6463
+ if (depositKey.length > 20) {
6464
+ result.depositKey.key = `${depositKey.slice(0, 10)}...${depositKey.slice(-8)}`;
6465
+ } else {
6466
+ result.depositKey.key = depositKey;
6467
+ }
6468
+ }
6469
+ const rpcUrl = options.rpcUrl || process.env.RPC_URL;
6470
+ if (rpcUrl) {
6471
+ result.rpcUrl.found = true;
6472
+ result.rpcUrl.url = rpcUrl;
6473
+ }
6474
+ const effectiveRpcUrl = rpcUrl || "https://mainnet.base.org";
6475
+ if (result.walletKey.found && result.walletKey.address) {
6476
+ result.registration.checked = true;
6477
+ try {
6478
+ const regStatus = await isRegistered(
6479
+ result.walletKey.address,
6480
+ effectiveRpcUrl
6481
+ );
6482
+ result.registration.registered = regStatus.registered;
6483
+ result.registration.onChainKey = regStatus.depositKey;
6484
+ if (regStatus.registered && depositKey && regStatus.depositKey) {
6485
+ result.registration.matches = regStatus.depositKey.toLowerCase() === depositKey.toLowerCase();
6486
+ }
6487
+ } catch (error) {
6488
+ result.registration.error = error instanceof Error ? error.message : "Unknown error";
6489
+ }
6490
+ }
6491
+ result.relay.checked = true;
6492
+ try {
6493
+ const health = await checkRelayHealth();
6494
+ result.relay.healthy = health.status === "ok";
6495
+ result.relay.status = health.status;
6496
+ result.relay.network = health.network;
6497
+ } catch (error) {
6498
+ result.relay.healthy = false;
6499
+ result.relay.error = error instanceof Error ? error.message : "Unknown error";
6500
+ }
6501
+ console.log(JSON.stringify(result, null, 2));
6502
+ });
6503
+ return status;
6504
+ }
6505
+
6417
6506
  // src/cli/index.ts
6418
6507
  loadEnv();
6419
6508
  var program2 = new Command();
@@ -6428,4 +6517,5 @@ program2.addCommand(createPrivateBalanceCommand());
6428
6517
  program2.addCommand(createWithdrawCommand());
6429
6518
  program2.addCommand(createTransferCommand());
6430
6519
  program2.addCommand(createMergeCommand());
6520
+ program2.addCommand(createStatusCommand());
6431
6521
  program2.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veil-cash/sdk",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
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",
@@ -25,14 +25,20 @@ function progress(msg: string, quiet?: boolean) {
25
25
  export function createDepositCommand(): Command {
26
26
  const deposit = new Command('deposit')
27
27
  .description('Deposit ETH into Veil')
28
+ .argument('<asset>', 'Asset to deposit (ETH)')
28
29
  .argument('<amount>', 'Amount to deposit (e.g., 0.1)')
29
30
  .option('--deposit-key <key>', 'Your Veil deposit key (or set DEPOSIT_KEY env)')
30
31
  .option('--wallet-key <key>', 'Ethereum wallet key for signing (or set WALLET_KEY env)')
31
32
  .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
32
33
  .option('--unsigned', 'Output unsigned transaction payload (Bankr-compatible format)')
33
34
  .option('--quiet', 'Suppress progress output')
34
- .action(async (amount: string, options) => {
35
+ .action(async (asset: string, amount: string, options) => {
35
36
  try {
37
+ // Validate asset is ETH
38
+ if (asset.toUpperCase() !== 'ETH') {
39
+ throw new CLIError(ErrorCode.INVALID_AMOUNT, 'Only ETH is supported');
40
+ }
41
+
36
42
  const amountNum = parseFloat(amount);
37
43
 
38
44
  // Check minimum deposit
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Status CLI command - Check configuration and service status
3
+ */
4
+
5
+ import { Command } from 'commander';
6
+ import { isRegistered, getAddress } from '../wallet.js';
7
+ import { checkRelayHealth } from '../../relay.js';
8
+
9
+ interface StatusResult {
10
+ walletKey: {
11
+ found: boolean;
12
+ address?: string;
13
+ };
14
+ veilKey: {
15
+ found: boolean;
16
+ };
17
+ depositKey: {
18
+ found: boolean;
19
+ key?: string;
20
+ };
21
+ rpcUrl: {
22
+ found: boolean;
23
+ url: string;
24
+ };
25
+ registration: {
26
+ checked: boolean;
27
+ registered?: boolean;
28
+ matches?: boolean;
29
+ onChainKey?: string | null;
30
+ error?: string;
31
+ };
32
+ relay: {
33
+ checked: boolean;
34
+ healthy?: boolean;
35
+ status?: string;
36
+ network?: string;
37
+ error?: string;
38
+ };
39
+ }
40
+
41
+ export function createStatusCommand(): Command {
42
+ const status = new Command('status')
43
+ .description('Check configuration and service status')
44
+ .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
45
+ .action(async (options) => {
46
+ const result: StatusResult = {
47
+ walletKey: { found: false },
48
+ veilKey: { found: false },
49
+ depositKey: { found: false },
50
+ rpcUrl: { found: false, url: 'https://mainnet.base.org' },
51
+ registration: { checked: false },
52
+ relay: { checked: false },
53
+ };
54
+
55
+ // Check WALLET_KEY
56
+ const walletKey = process.env.WALLET_KEY;
57
+ if (walletKey) {
58
+ result.walletKey.found = true;
59
+ try {
60
+ result.walletKey.address = getAddress(walletKey as `0x${string}`);
61
+ } catch {
62
+ // Invalid key format, but it was found
63
+ }
64
+ }
65
+
66
+ // Check VEIL_KEY
67
+ const veilKey = process.env.VEIL_KEY;
68
+ if (veilKey) {
69
+ result.veilKey.found = true;
70
+ }
71
+
72
+ // Check DEPOSIT_KEY
73
+ const depositKey = process.env.DEPOSIT_KEY;
74
+ if (depositKey) {
75
+ result.depositKey.found = true;
76
+ // Show truncated key
77
+ if (depositKey.length > 20) {
78
+ result.depositKey.key = `${depositKey.slice(0, 10)}...${depositKey.slice(-8)}`;
79
+ } else {
80
+ result.depositKey.key = depositKey;
81
+ }
82
+ }
83
+
84
+ // Check RPC_URL
85
+ const rpcUrl = options.rpcUrl || process.env.RPC_URL;
86
+ if (rpcUrl) {
87
+ result.rpcUrl.found = true;
88
+ result.rpcUrl.url = rpcUrl;
89
+ }
90
+ const effectiveRpcUrl = rpcUrl || 'https://mainnet.base.org';
91
+
92
+ // Check registration status (requires wallet address)
93
+ if (result.walletKey.found && result.walletKey.address) {
94
+ result.registration.checked = true;
95
+ try {
96
+ const regStatus = await isRegistered(
97
+ result.walletKey.address as `0x${string}`,
98
+ effectiveRpcUrl
99
+ );
100
+ result.registration.registered = regStatus.registered;
101
+ result.registration.onChainKey = regStatus.depositKey;
102
+
103
+ // Check if on-chain key matches env DEPOSIT_KEY
104
+ if (regStatus.registered && depositKey && regStatus.depositKey) {
105
+ result.registration.matches =
106
+ regStatus.depositKey.toLowerCase() === depositKey.toLowerCase();
107
+ }
108
+ } catch (error) {
109
+ result.registration.error = error instanceof Error ? error.message : 'Unknown error';
110
+ }
111
+ }
112
+
113
+ // Check relay health
114
+ result.relay.checked = true;
115
+ try {
116
+ const health = await checkRelayHealth();
117
+ result.relay.healthy = health.status === 'ok';
118
+ result.relay.status = health.status;
119
+ result.relay.network = health.network;
120
+ } catch (error) {
121
+ result.relay.healthy = false;
122
+ result.relay.error = error instanceof Error ? error.message : 'Unknown error';
123
+ }
124
+
125
+ console.log(JSON.stringify(result, null, 2));
126
+ });
127
+
128
+ return status;
129
+ }
@@ -17,13 +17,19 @@ function progress(msg: string, quiet?: boolean) {
17
17
  export function createTransferCommand(): Command {
18
18
  const transferCmd = new Command('transfer')
19
19
  .description('Transfer privately within the pool to another registered address')
20
+ .argument('<asset>', 'Asset to transfer (ETH)')
20
21
  .argument('<amount>', 'Amount to transfer (e.g., 0.1)')
21
22
  .argument('<recipient>', 'Recipient address (must be registered)')
22
23
  .option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
23
24
  .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
24
25
  .option('--quiet', 'Suppress progress output')
25
- .action(async (amount: string, recipient: string, options) => {
26
+ .action(async (asset: string, amount: string, recipient: string, options) => {
26
27
  try {
28
+ // Validate asset is ETH
29
+ if (asset.toUpperCase() !== 'ETH') {
30
+ throw new CLIError(ErrorCode.INVALID_AMOUNT, 'Only ETH is supported');
31
+ }
32
+
27
33
  // Validate recipient
28
34
  if (!/^0x[a-fA-F0-9]{40}$/.test(recipient)) {
29
35
  throw new CLIError(ErrorCode.INVALID_ADDRESS, 'Invalid recipient address format');
@@ -82,12 +88,18 @@ export function createTransferCommand(): Command {
82
88
  export function createMergeCommand(): Command {
83
89
  const mergeCmd = new Command('merge')
84
90
  .description('Merge UTXOs by self-transfer (consolidate small UTXOs)')
91
+ .argument('<asset>', 'Asset to merge (ETH)')
85
92
  .argument('<amount>', 'Amount to merge (e.g., 0.5)')
86
93
  .option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
87
94
  .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
88
95
  .option('--quiet', 'Suppress progress output')
89
- .action(async (amount: string, options) => {
96
+ .action(async (asset: string, amount: string, options) => {
90
97
  try {
98
+ // Validate asset is ETH
99
+ if (asset.toUpperCase() !== 'ETH') {
100
+ throw new CLIError(ErrorCode.INVALID_AMOUNT, 'Only ETH is supported');
101
+ }
102
+
91
103
  // Get keypair
92
104
  const veilKey = options.veilKey || process.env.VEIL_KEY;
93
105
  if (!veilKey) {
@@ -17,13 +17,19 @@ function progress(msg: string, quiet?: boolean) {
17
17
  export function createWithdrawCommand(): Command {
18
18
  const withdrawCmd = new Command('withdraw')
19
19
  .description('Withdraw from private pool to a public address')
20
+ .argument('<asset>', 'Asset to withdraw (ETH)')
20
21
  .argument('<amount>', 'Amount to withdraw (e.g., 0.1)')
21
22
  .argument('<recipient>', 'Recipient address (e.g., 0x...)')
22
23
  .option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
23
24
  .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
24
25
  .option('--quiet', 'Suppress progress output')
25
- .action(async (amount: string, recipient: string, options) => {
26
+ .action(async (asset: string, amount: string, recipient: string, options) => {
26
27
  try {
28
+ // Validate asset is ETH
29
+ if (asset.toUpperCase() !== 'ETH') {
30
+ throw new CLIError(ErrorCode.INVALID_AMOUNT, 'Only ETH is supported');
31
+ }
32
+
27
33
  // Validate recipient
28
34
  if (!/^0x[a-fA-F0-9]{40}$/.test(recipient)) {
29
35
  throw new CLIError(ErrorCode.INVALID_ADDRESS, 'Invalid recipient address format');
package/src/cli/index.ts CHANGED
@@ -2,16 +2,17 @@
2
2
  * Veil CLI - Command-line interface for Veil Cash
3
3
  *
4
4
  * Usage:
5
- * veil init # Generate keypair
6
- * veil keypair # Show keypair (JSON)
7
- * veil register # Register on-chain
8
- * veil deposit 0.1 # Deposit ETH
9
- * veil balance # Show all balances
10
- * veil queue-balance # Show pending queue deposits
11
- * veil private-balance # Show private balance
12
- * veil withdraw 0.1 --recipient 0x... # Withdraw to public address
13
- * veil transfer 0.1 --to 0x... # Transfer privately
14
- * veil merge 0.5 # Merge UTXOs (self-transfer)
5
+ * veil init # Generate keypair
6
+ * veil keypair # Show keypair (JSON)
7
+ * veil status # Check config and service status
8
+ * veil register # Register on-chain
9
+ * veil deposit ETH 0.1 # Deposit ETH
10
+ * veil balance # Show all balances
11
+ * veil queue-balance # Show pending queue deposits
12
+ * veil private-balance # Show private balance
13
+ * veil withdraw ETH 0.1 0x... # Withdraw to public address
14
+ * veil transfer ETH 0.1 0x... # Transfer privately
15
+ * veil merge ETH 0.5 # Merge UTXOs (self-transfer)
15
16
  */
16
17
 
17
18
  import { Command } from 'commander';
@@ -25,6 +26,7 @@ import { createQueueBalanceCommand } from './commands/queue-balance.js';
25
26
  import { createPrivateBalanceCommand } from './commands/private-balance.js';
26
27
  import { createWithdrawCommand } from './commands/withdraw.js';
27
28
  import { createTransferCommand, createMergeCommand } from './commands/transfer.js';
29
+ import { createStatusCommand } from './commands/status.js';
28
30
 
29
31
  // Load environment variables
30
32
  loadEnv();
@@ -47,6 +49,7 @@ program.addCommand(createPrivateBalanceCommand());
47
49
  program.addCommand(createWithdrawCommand());
48
50
  program.addCommand(createTransferCommand());
49
51
  program.addCommand(createMergeCommand());
52
+ program.addCommand(createStatusCommand());
50
53
 
51
54
  // Parse and execute
52
55
  program.parse();