@veil-cash/sdk 0.1.0 → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Veil Cash
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # @veil-cash/sdk
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@veil-cash/sdk.svg)](https://www.npmjs.com/package/@veil-cash/sdk)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@veil-cash/sdk.svg)](https://www.npmjs.com/package/@veil-cash/sdk)
5
+ [![license](https://img.shields.io/npm/l/@veil-cash/sdk.svg)](https://github.com/veildotcash/veildotcash-sdk/blob/main/LICENSE)
6
+
3
7
  SDK and CLI for interacting with [Veil Cash](https://veil.cash) privacy pools on Base.
4
8
 
5
- Generate keypairs, register, deposit, withdraw, and transfer ETH privately.
9
+ Generate keypairs, register, deposit, withdraw, transfer, and merge ETH privately.
6
10
 
7
11
  ## Installation
8
12
 
@@ -31,17 +35,23 @@ export WALLET_KEY=0x...
31
35
  # 3. Register your deposit key (one-time)
32
36
  veil register
33
37
 
34
- # 4. Deposit ETH
35
- veil deposit 0.1
38
+ # 4. Check your setup
39
+ veil status
40
+
41
+ # 5. Deposit ETH
42
+ veil deposit ETH 0.1
36
43
 
37
- # 5. Check your balance
44
+ # 6. Check your balance
38
45
  veil balance
39
46
 
40
- # 6. Withdraw to any address
41
- veil withdraw 0.05 0xRecipientAddress
47
+ # 7. Withdraw to any address
48
+ veil withdraw ETH 0.05 0xRecipientAddress
42
49
 
43
- # 7. Transfer privately to another registered user
44
- veil transfer 0.02 0xRecipientAddress
50
+ # 8. Transfer privately to another registered user
51
+ veil transfer ETH 0.02 0xRecipientAddress
52
+
53
+ # 9. Merge small UTXOs (consolidate balances)
54
+ veil merge ETH 0.1
45
55
  ```
46
56
 
47
57
  ## CLI Commands
@@ -60,13 +70,43 @@ veil init --no-save # Print keypair without saving
60
70
 
61
71
  ### `veil keypair`
62
72
 
63
- Generate and show a new keypair as JSON (does not save).
73
+ Show current Veil keypair as JSON (from VEIL_KEY env).
64
74
 
65
75
  ```bash
66
76
  veil keypair
67
77
  # {"veilPrivateKey":"0x...","depositKey":"0x..."}
68
78
  ```
69
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
+
70
110
  ### `veil register`
71
111
 
72
112
  Register your deposit key on-chain (one-time per address).
@@ -77,14 +117,14 @@ veil register --json # JSON output
77
117
  veil register --unsigned --address 0x... # Unsigned payload for agents
78
118
  ```
79
119
 
80
- ### `veil deposit <amount>`
120
+ ### `veil deposit ETH <amount>`
81
121
 
82
122
  Deposit ETH into the privacy pool.
83
123
 
84
124
  ```bash
85
- veil deposit 0.1 # Signs & sends (JSON output)
86
- veil deposit 0.1 --unsigned # Unsigned payload for agents
87
- 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
88
128
  ```
89
129
 
90
130
  Output:
@@ -134,13 +174,13 @@ Output:
134
174
  }
135
175
  ```
136
176
 
137
- ### `veil withdraw <amount> <recipient>`
177
+ ### `veil withdraw ETH <amount> <recipient>`
138
178
 
139
179
  Withdraw from the privacy pool to any public address.
140
180
 
141
181
  ```bash
142
- veil withdraw 0.05 0xRecipientAddress
143
- veil withdraw 0.05 0xRecipientAddress --quiet
182
+ veil withdraw ETH 0.05 0xRecipientAddress
183
+ veil withdraw ETH 0.05 0xRecipientAddress --quiet
144
184
  ```
145
185
 
146
186
  Output:
@@ -155,13 +195,13 @@ Output:
155
195
  }
156
196
  ```
157
197
 
158
- ### `veil transfer <amount> <recipient>`
198
+ ### `veil transfer ETH <amount> <recipient>`
159
199
 
160
200
  Transfer privately to another registered Veil user.
161
201
 
162
202
  ```bash
163
- veil transfer 0.02 0xRecipientAddress
164
- veil transfer 0.02 0xRecipientAddress --quiet
203
+ veil transfer ETH 0.02 0xRecipientAddress
204
+ veil transfer ETH 0.02 0xRecipientAddress --quiet
165
205
  ```
166
206
 
167
207
  Output:
@@ -176,13 +216,24 @@ Output:
176
216
  }
177
217
  ```
178
218
 
179
- ### `veil merge <amount>`
219
+ ### `veil merge ETH <amount>`
180
220
 
181
221
  Consolidate multiple small UTXOs into one (self-transfer).
182
222
 
183
223
  ```bash
184
- veil merge 0.1 # Merge UTXOs totaling 0.1 ETH
185
- 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
+ }
186
237
  ```
187
238
 
188
239
  ## Environment Variables
@@ -387,11 +438,11 @@ veil init --json
387
438
 
388
439
  # Get unsigned transaction payloads for agent signing
389
440
  veil register --unsigned --address 0x...
390
- veil deposit 0.1 --unsigned
441
+ veil deposit ETH 0.1 --unsigned
391
442
 
392
443
  # Suppress progress output for clean JSON
393
444
  veil balance --quiet
394
- veil withdraw 0.05 0xRecipient --quiet
445
+ veil withdraw ETH 0.05 0xRecipient --quiet
395
446
  ```
396
447
 
397
448
  ### Bankr Integration
@@ -399,7 +450,7 @@ veil withdraw 0.05 0xRecipient --quiet
399
450
  Use `--unsigned` to get Bankr-compatible transaction payloads:
400
451
 
401
452
  ```bash
402
- veil deposit 0.1 --unsigned
453
+ veil deposit ETH 0.1 --unsigned
403
454
  # {"to":"0x...","data":"0x...","value":"100000000000000000","chainId":8453}
404
455
  ```
405
456
 
@@ -431,14 +482,15 @@ const result = await withdraw({
431
482
 
432
483
  1. **Generate Keypair**: Run `veil init` to create and save your Veil keypair
433
484
  2. **Register**: Run `veil register` to link your deposit key on-chain (one-time)
434
- 3. **Deposit**: Run `veil deposit <amount>` to send ETH
435
- 4. **Wait**: The Veil deposit engine processes your deposit
436
- 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
437
489
 
438
490
  ## Withdrawal Flow
439
491
 
440
492
  1. **Check Balance**: Run `veil balance` to see your private balance
441
- 2. **Withdraw**: Run `veil withdraw <amount> <recipient>`
493
+ 2. **Withdraw**: Run `veil withdraw ETH <amount> <recipient>`
442
494
  3. **Done**: The SDK builds ZK proofs and submits via the relayer
443
495
 
444
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.0",
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();