@veil-cash/sdk 0.3.0 → 0.5.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 +105 -512
- package/SDK.md +311 -0
- package/dist/cli/index.cjs +1446 -978
- package/dist/index.cjs +9 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -58
- package/dist/index.d.ts +6 -58
- package/dist/index.js +10 -62
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
- package/skills/veil/SKILL.md +526 -0
- package/skills/veil/reference.md +231 -0
- package/src/abi.ts +0 -12
- package/src/addresses.ts +8 -15
- package/src/balance.ts +4 -4
- package/src/cli/commands/balance.ts +128 -40
- package/src/cli/commands/deposit.ts +140 -71
- package/src/cli/commands/init.ts +98 -68
- package/src/cli/commands/keypair.ts +34 -16
- package/src/cli/commands/private-balance.ts +38 -36
- package/src/cli/commands/queue-balance.ts +49 -37
- package/src/cli/commands/register.ts +67 -53
- package/src/cli/commands/status.ts +196 -70
- package/src/cli/commands/transfer.ts +65 -56
- package/src/cli/commands/withdraw.ts +34 -32
- package/src/cli/config.ts +85 -5
- package/src/cli/errors.ts +4 -0
- package/src/cli/index.ts +23 -5
- package/src/cli/output.ts +87 -0
- package/src/cli/wallet.ts +75 -16
- package/src/deposit.ts +1 -70
- package/src/index.ts +6 -3
- package/src/prover.ts +3 -0
- package/src/relay.ts +2 -2
- package/src/types.ts +2 -5
|
@@ -3,13 +3,28 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Command } from 'commander';
|
|
6
|
-
import { isRegistered, getAddress } from '../wallet.js';
|
|
6
|
+
import { isRegistered, getAddress, getWalletBalances } from '../wallet.js';
|
|
7
|
+
import { resolveAddress } from '../config.js';
|
|
7
8
|
import { checkRelayHealth } from '../../relay.js';
|
|
9
|
+
import { handleCLIError } from '../errors.js';
|
|
10
|
+
import { maskValue, printFields, printHeader, printJson, printSection } from '../output.js';
|
|
8
11
|
|
|
9
12
|
interface StatusResult {
|
|
10
13
|
walletKey: {
|
|
11
14
|
found: boolean;
|
|
12
15
|
address?: string;
|
|
16
|
+
valid?: boolean;
|
|
17
|
+
ethBalance?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
};
|
|
20
|
+
signerAddress: {
|
|
21
|
+
found: boolean;
|
|
22
|
+
address?: string;
|
|
23
|
+
valid?: boolean;
|
|
24
|
+
};
|
|
25
|
+
resolvedAddress: {
|
|
26
|
+
address?: string;
|
|
27
|
+
source?: string;
|
|
13
28
|
};
|
|
14
29
|
veilKey: {
|
|
15
30
|
found: boolean;
|
|
@@ -41,89 +56,200 @@ interface StatusResult {
|
|
|
41
56
|
export function createStatusCommand(): Command {
|
|
42
57
|
const status = new Command('status')
|
|
43
58
|
.description('Check configuration and service status')
|
|
44
|
-
.option('--
|
|
59
|
+
.option('--json', 'Output as JSON')
|
|
60
|
+
.addHelpText('after', `
|
|
61
|
+
Examples:
|
|
62
|
+
veil status
|
|
63
|
+
veil status --json
|
|
64
|
+
`)
|
|
45
65
|
.action(async (options) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
try {
|
|
67
|
+
const result: StatusResult = {
|
|
68
|
+
walletKey: { found: false },
|
|
69
|
+
signerAddress: { found: false },
|
|
70
|
+
resolvedAddress: {},
|
|
71
|
+
veilKey: { found: false },
|
|
72
|
+
depositKey: { found: false },
|
|
73
|
+
rpcUrl: { found: false, url: 'https://mainnet.base.org' },
|
|
74
|
+
registration: { checked: false },
|
|
75
|
+
relay: { checked: false },
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Check WALLET_KEY
|
|
79
|
+
const walletKey = process.env.WALLET_KEY;
|
|
80
|
+
if (walletKey) {
|
|
81
|
+
result.walletKey.found = true;
|
|
82
|
+
try {
|
|
83
|
+
result.walletKey.address = getAddress(walletKey as `0x${string}`);
|
|
84
|
+
result.walletKey.valid = true;
|
|
85
|
+
} catch {
|
|
86
|
+
result.walletKey.valid = false;
|
|
87
|
+
result.walletKey.error = 'invalid format';
|
|
88
|
+
}
|
|
63
89
|
}
|
|
64
|
-
}
|
|
65
90
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
91
|
+
// Check SIGNER_ADDRESS
|
|
92
|
+
const signerAddress = process.env.SIGNER_ADDRESS;
|
|
93
|
+
if (signerAddress) {
|
|
94
|
+
result.signerAddress.found = true;
|
|
95
|
+
if (/^0x[a-fA-F0-9]{40}$/.test(signerAddress.trim())) {
|
|
96
|
+
result.signerAddress.valid = true;
|
|
97
|
+
result.signerAddress.address = signerAddress.trim();
|
|
98
|
+
} else {
|
|
99
|
+
result.signerAddress.valid = false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
71
102
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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;
|
|
103
|
+
// Check VEIL_KEY
|
|
104
|
+
const veilKey = process.env.VEIL_KEY;
|
|
105
|
+
if (veilKey) {
|
|
106
|
+
result.veilKey.found = true;
|
|
81
107
|
}
|
|
82
|
-
}
|
|
83
108
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
// Check DEPOSIT_KEY
|
|
110
|
+
const depositKey = process.env.DEPOSIT_KEY;
|
|
111
|
+
if (depositKey) {
|
|
112
|
+
result.depositKey.found = true;
|
|
113
|
+
if (depositKey.length > 20) {
|
|
114
|
+
result.depositKey.key = `${depositKey.slice(0, 10)}...${depositKey.slice(-8)}`;
|
|
115
|
+
} else {
|
|
116
|
+
result.depositKey.key = depositKey;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
91
119
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
120
|
+
// Check RPC_URL
|
|
121
|
+
const rpcUrl = process.env.RPC_URL;
|
|
122
|
+
if (rpcUrl) {
|
|
123
|
+
result.rpcUrl.found = true;
|
|
124
|
+
result.rpcUrl.url = rpcUrl;
|
|
125
|
+
}
|
|
126
|
+
const effectiveRpcUrl = rpcUrl || 'https://mainnet.base.org';
|
|
127
|
+
|
|
128
|
+
const resolvedAddress = resolveAddress({}, { required: false, allowInvalidWalletKey: true });
|
|
129
|
+
if (resolvedAddress) {
|
|
130
|
+
result.resolvedAddress.address = resolvedAddress.address;
|
|
131
|
+
result.resolvedAddress.source =
|
|
132
|
+
resolvedAddress.source === 'wallet-key'
|
|
133
|
+
? 'WALLET_KEY'
|
|
134
|
+
: resolvedAddress.source === 'signer-address'
|
|
135
|
+
? 'SIGNER_ADDRESS'
|
|
136
|
+
: '--address';
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const walletBalances = await getWalletBalances(
|
|
140
|
+
resolvedAddress.address,
|
|
141
|
+
effectiveRpcUrl
|
|
142
|
+
);
|
|
143
|
+
result.walletKey.ethBalance = walletBalances.eth;
|
|
144
|
+
} catch {
|
|
145
|
+
// Ignore wallet balance lookup errors so status can still report config/registration
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
result.registration.checked = true;
|
|
149
|
+
try {
|
|
150
|
+
const regStatus = await isRegistered(
|
|
151
|
+
resolvedAddress.address,
|
|
152
|
+
effectiveRpcUrl
|
|
153
|
+
);
|
|
154
|
+
result.registration.registered = regStatus.registered;
|
|
155
|
+
result.registration.onChainKey = regStatus.depositKey;
|
|
156
|
+
|
|
157
|
+
if (regStatus.registered && depositKey && regStatus.depositKey) {
|
|
158
|
+
result.registration.matches =
|
|
159
|
+
regStatus.depositKey.toLowerCase() === depositKey.toLowerCase();
|
|
160
|
+
}
|
|
161
|
+
} catch (error) {
|
|
162
|
+
result.registration.error = error instanceof Error ? error.message : 'Unknown error';
|
|
107
163
|
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Check relay health
|
|
167
|
+
const relayUrl = process.env.RELAY_URL;
|
|
168
|
+
result.relay.checked = true;
|
|
169
|
+
try {
|
|
170
|
+
const health = await checkRelayHealth(relayUrl);
|
|
171
|
+
result.relay.healthy = health.status === 'ok';
|
|
172
|
+
result.relay.status = health.status;
|
|
173
|
+
result.relay.network = health.network;
|
|
108
174
|
} catch (error) {
|
|
109
|
-
result.
|
|
175
|
+
result.relay.healthy = false;
|
|
176
|
+
result.relay.error = error instanceof Error ? error.message : 'Unknown error';
|
|
110
177
|
}
|
|
111
|
-
}
|
|
112
178
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
179
|
+
if (options.json) {
|
|
180
|
+
printJson(result);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const signingValue = result.signerAddress.found
|
|
185
|
+
? result.signerAddress.valid === false
|
|
186
|
+
? 'external (SIGNER_ADDRESS invalid)'
|
|
187
|
+
: 'external (SIGNER_ADDRESS)'
|
|
188
|
+
: result.walletKey.found
|
|
189
|
+
? result.walletKey.valid === false
|
|
190
|
+
? 'local (WALLET_KEY invalid)'
|
|
191
|
+
: 'local (WALLET_KEY)'
|
|
192
|
+
: 'not configured';
|
|
193
|
+
|
|
194
|
+
printHeader('Veil CLI Status');
|
|
195
|
+
printSection('Configuration');
|
|
196
|
+
printFields([
|
|
197
|
+
{ label: 'Signing', value: signingValue },
|
|
198
|
+
{ label: 'Address', value: result.resolvedAddress.address || 'n/a' },
|
|
199
|
+
{ label: 'ETH balance', value: result.walletKey.ethBalance ? `${result.walletKey.ethBalance} ETH` : 'n/a' },
|
|
200
|
+
{ label: 'Veil key', value: result.veilKey.found ? 'configured' : 'missing' },
|
|
201
|
+
{ label: 'Deposit key', value: result.depositKey.found ? maskValue(result.depositKey.key || '') : 'missing' },
|
|
202
|
+
{ label: 'RPC URL', value: maskUrl(result.rpcUrl.url) },
|
|
203
|
+
]);
|
|
204
|
+
|
|
205
|
+
printSection('Registration');
|
|
206
|
+
if (!result.registration.checked) {
|
|
207
|
+
printFields([
|
|
208
|
+
{
|
|
209
|
+
label: 'Status',
|
|
210
|
+
value: result.walletKey.found && result.walletKey.valid === false
|
|
211
|
+
? 'skipped (invalid WALLET_KEY)'
|
|
212
|
+
: 'skipped (no WALLET_KEY or SIGNER_ADDRESS)'
|
|
213
|
+
},
|
|
214
|
+
]);
|
|
215
|
+
} else if (result.registration.error) {
|
|
216
|
+
printFields([
|
|
217
|
+
{ label: 'Error', value: result.registration.error },
|
|
218
|
+
]);
|
|
219
|
+
} else {
|
|
220
|
+
printFields([
|
|
221
|
+
{ label: 'Registered', value: result.registration.registered },
|
|
222
|
+
{ label: 'Keys match', value: result.registration.matches ?? 'n/a' },
|
|
223
|
+
{ label: 'On-chain key', value: result.registration.onChainKey ? maskValue(result.registration.onChainKey) : 'n/a' },
|
|
224
|
+
]);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
printSection('Relay');
|
|
228
|
+
if (result.relay.error) {
|
|
229
|
+
printFields([
|
|
230
|
+
{ label: 'Healthy', value: false },
|
|
231
|
+
{ label: 'Error', value: result.relay.error },
|
|
232
|
+
]);
|
|
233
|
+
} else {
|
|
234
|
+
printFields([
|
|
235
|
+
{ label: 'Healthy', value: result.relay.healthy },
|
|
236
|
+
{ label: 'Status', value: result.relay.status || 'n/a' },
|
|
237
|
+
{ label: 'Network', value: result.relay.network || 'n/a' },
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
120
240
|
} catch (error) {
|
|
121
|
-
|
|
122
|
-
result.relay.error = error instanceof Error ? error.message : 'Unknown error';
|
|
241
|
+
handleCLIError(error);
|
|
123
242
|
}
|
|
124
|
-
|
|
125
|
-
console.log(JSON.stringify(result, null, 2));
|
|
126
243
|
});
|
|
127
244
|
|
|
128
245
|
return status;
|
|
129
246
|
}
|
|
247
|
+
|
|
248
|
+
function maskUrl(url: string): string {
|
|
249
|
+
try {
|
|
250
|
+
const parsed = new URL(url);
|
|
251
|
+
return parsed.origin;
|
|
252
|
+
} catch {
|
|
253
|
+
return maskValue(url);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -6,26 +6,24 @@ import { Command } from 'commander';
|
|
|
6
6
|
import { Keypair } from '../../keypair.js';
|
|
7
7
|
import { transfer, mergeUtxos } from '../../transfer.js';
|
|
8
8
|
import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
|
|
9
|
+
import { clearProgress, createProgressReporter, printFields, printHeader, printJson, printLine, txUrl } from '../output.js';
|
|
9
10
|
import type { RelayPool } from '../../types.js';
|
|
10
11
|
|
|
11
|
-
const SUPPORTED_ASSETS = ['ETH', 'USDC'
|
|
12
|
-
|
|
13
|
-
// Progress helper - writes to stderr so JSON output stays clean
|
|
14
|
-
function progress(msg: string, quiet?: boolean) {
|
|
15
|
-
if (!quiet) {
|
|
16
|
-
process.stderr.write(`\r\x1b[K${msg}`);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
12
|
+
const SUPPORTED_ASSETS = ['ETH', 'USDC'];
|
|
19
13
|
|
|
20
14
|
export function createTransferCommand(): Command {
|
|
21
15
|
const transferCmd = new Command('transfer')
|
|
22
16
|
.description('Transfer privately within the pool to another registered address')
|
|
23
|
-
.argument('<asset>', 'Asset to transfer (ETH
|
|
17
|
+
.argument('<asset>', 'Asset to transfer (ETH or USDC)')
|
|
24
18
|
.argument('<amount>', 'Amount to transfer (e.g., 0.1)')
|
|
25
19
|
.argument('<recipient>', 'Recipient address (must be registered)')
|
|
26
|
-
.option('--
|
|
27
|
-
.
|
|
28
|
-
|
|
20
|
+
.option('--json', 'Output as JSON')
|
|
21
|
+
.addHelpText('after', `
|
|
22
|
+
Examples:
|
|
23
|
+
veil transfer ETH 0.02 0xRecipientAddress
|
|
24
|
+
veil transfer USDC 25 0xRecipientAddress
|
|
25
|
+
veil transfer ETH 0.02 0xRecipientAddress --json
|
|
26
|
+
`)
|
|
29
27
|
.action(async (asset: string, amount: string, recipient: string, options) => {
|
|
30
28
|
try {
|
|
31
29
|
const assetUpper = asset.toUpperCase();
|
|
@@ -41,24 +39,16 @@ export function createTransferCommand(): Command {
|
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
// Get keypair
|
|
44
|
-
const veilKey =
|
|
42
|
+
const veilKey = process.env.VEIL_KEY;
|
|
45
43
|
if (!veilKey) {
|
|
46
|
-
throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required.
|
|
44
|
+
throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required. Set VEIL_KEY env');
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
const senderKeypair = new Keypair(veilKey);
|
|
50
|
-
const rpcUrl =
|
|
48
|
+
const rpcUrl = process.env.RPC_URL;
|
|
51
49
|
const pool = assetUpper.toLowerCase() as RelayPool;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const onProgress = options.quiet
|
|
55
|
-
? undefined
|
|
56
|
-
: (stage: string, detail?: string) => {
|
|
57
|
-
const msg = detail ? `${stage}: ${detail}` : stage;
|
|
58
|
-
progress(msg, options.quiet);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
progress(`Starting ${assetUpper} transfer...`, options.quiet);
|
|
50
|
+
const onProgress = createProgressReporter();
|
|
51
|
+
onProgress(`Starting ${assetUpper} transfer...`);
|
|
62
52
|
|
|
63
53
|
// Execute transfer
|
|
64
54
|
const result = await transfer({
|
|
@@ -70,11 +60,9 @@ export function createTransferCommand(): Command {
|
|
|
70
60
|
onProgress,
|
|
71
61
|
});
|
|
72
62
|
|
|
73
|
-
|
|
74
|
-
progress('', options.quiet);
|
|
63
|
+
clearProgress();
|
|
75
64
|
|
|
76
|
-
|
|
77
|
-
console.log(JSON.stringify({
|
|
65
|
+
const output = {
|
|
78
66
|
success: result.success,
|
|
79
67
|
transactionHash: result.transactionHash,
|
|
80
68
|
blockNumber: result.blockNumber,
|
|
@@ -82,10 +70,24 @@ export function createTransferCommand(): Command {
|
|
|
82
70
|
amount: result.amount,
|
|
83
71
|
recipient: result.recipient,
|
|
84
72
|
type: 'transfer',
|
|
85
|
-
}
|
|
86
|
-
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
if (options.json) {
|
|
76
|
+
printJson(output);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
printHeader('Transfer Submitted');
|
|
81
|
+
printFields([
|
|
82
|
+
{ label: 'Asset', value: assetUpper },
|
|
83
|
+
{ label: 'Amount', value: result.amount },
|
|
84
|
+
{ label: 'Recipient', value: result.recipient },
|
|
85
|
+
{ label: 'Transaction', value: txUrl(result.transactionHash) },
|
|
86
|
+
{ label: 'Block', value: result.blockNumber },
|
|
87
|
+
]);
|
|
88
|
+
printLine();
|
|
87
89
|
} catch (error) {
|
|
88
|
-
|
|
90
|
+
clearProgress();
|
|
89
91
|
handleCLIError(error);
|
|
90
92
|
}
|
|
91
93
|
});
|
|
@@ -96,11 +98,15 @@ export function createTransferCommand(): Command {
|
|
|
96
98
|
export function createMergeCommand(): Command {
|
|
97
99
|
const mergeCmd = new Command('merge')
|
|
98
100
|
.description('Merge UTXOs by self-transfer (consolidate small UTXOs)')
|
|
99
|
-
.argument('<asset>', 'Asset to merge (ETH
|
|
101
|
+
.argument('<asset>', 'Asset to merge (ETH or USDC)')
|
|
100
102
|
.argument('<amount>', 'Amount to merge (e.g., 0.5)')
|
|
101
|
-
.option('--
|
|
102
|
-
.
|
|
103
|
-
|
|
103
|
+
.option('--json', 'Output as JSON')
|
|
104
|
+
.addHelpText('after', `
|
|
105
|
+
Examples:
|
|
106
|
+
veil merge ETH 0.1
|
|
107
|
+
veil merge USDC 100
|
|
108
|
+
veil merge ETH 0.1 --json
|
|
109
|
+
`)
|
|
104
110
|
.action(async (asset: string, amount: string, options) => {
|
|
105
111
|
try {
|
|
106
112
|
const assetUpper = asset.toUpperCase();
|
|
@@ -111,24 +117,16 @@ export function createMergeCommand(): Command {
|
|
|
111
117
|
}
|
|
112
118
|
|
|
113
119
|
// Get keypair
|
|
114
|
-
const veilKey =
|
|
120
|
+
const veilKey = process.env.VEIL_KEY;
|
|
115
121
|
if (!veilKey) {
|
|
116
|
-
throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required.
|
|
122
|
+
throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required. Set VEIL_KEY env');
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
const keypair = new Keypair(veilKey);
|
|
120
|
-
const rpcUrl =
|
|
126
|
+
const rpcUrl = process.env.RPC_URL;
|
|
121
127
|
const pool = assetUpper.toLowerCase() as RelayPool;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const onProgress = options.quiet
|
|
125
|
-
? undefined
|
|
126
|
-
: (stage: string, detail?: string) => {
|
|
127
|
-
const msg = detail ? `${stage}: ${detail}` : stage;
|
|
128
|
-
progress(msg, options.quiet);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
progress(`Starting ${assetUpper} merge (self-transfer)...`, options.quiet);
|
|
128
|
+
const onProgress = createProgressReporter();
|
|
129
|
+
onProgress(`Starting ${assetUpper} merge (self-transfer)...`);
|
|
132
130
|
|
|
133
131
|
// Execute merge
|
|
134
132
|
const result = await mergeUtxos({
|
|
@@ -139,21 +137,32 @@ export function createMergeCommand(): Command {
|
|
|
139
137
|
onProgress,
|
|
140
138
|
});
|
|
141
139
|
|
|
142
|
-
|
|
143
|
-
progress('', options.quiet);
|
|
140
|
+
clearProgress();
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
console.log(JSON.stringify({
|
|
142
|
+
const output = {
|
|
147
143
|
success: result.success,
|
|
148
144
|
transactionHash: result.transactionHash,
|
|
149
145
|
blockNumber: result.blockNumber,
|
|
150
146
|
asset: assetUpper,
|
|
151
147
|
amount: result.amount,
|
|
152
148
|
type: 'merge',
|
|
153
|
-
}
|
|
154
|
-
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
if (options.json) {
|
|
152
|
+
printJson(output);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
printHeader('Merge Submitted');
|
|
157
|
+
printFields([
|
|
158
|
+
{ label: 'Asset', value: assetUpper },
|
|
159
|
+
{ label: 'Amount', value: result.amount },
|
|
160
|
+
{ label: 'Transaction', value: txUrl(result.transactionHash) },
|
|
161
|
+
{ label: 'Block', value: result.blockNumber },
|
|
162
|
+
]);
|
|
163
|
+
printLine();
|
|
155
164
|
} catch (error) {
|
|
156
|
-
|
|
165
|
+
clearProgress();
|
|
157
166
|
handleCLIError(error);
|
|
158
167
|
}
|
|
159
168
|
});
|
|
@@ -6,26 +6,24 @@ import { Command } from 'commander';
|
|
|
6
6
|
import { Keypair } from '../../keypair.js';
|
|
7
7
|
import { withdraw } from '../../withdraw.js';
|
|
8
8
|
import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
|
|
9
|
+
import { clearProgress, createProgressReporter, printFields, printHeader, printJson, printLine, txUrl } from '../output.js';
|
|
9
10
|
import type { RelayPool } from '../../types.js';
|
|
10
11
|
|
|
11
|
-
const SUPPORTED_ASSETS = ['ETH', 'USDC'
|
|
12
|
-
|
|
13
|
-
// Progress helper - writes to stderr so JSON output stays clean
|
|
14
|
-
function progress(msg: string, quiet?: boolean) {
|
|
15
|
-
if (!quiet) {
|
|
16
|
-
process.stderr.write(`\r\x1b[K${msg}`);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
12
|
+
const SUPPORTED_ASSETS = ['ETH', 'USDC'];
|
|
19
13
|
|
|
20
14
|
export function createWithdrawCommand(): Command {
|
|
21
15
|
const withdrawCmd = new Command('withdraw')
|
|
22
16
|
.description('Withdraw from private pool to a public address')
|
|
23
|
-
.argument('<asset>', 'Asset to withdraw (ETH
|
|
17
|
+
.argument('<asset>', 'Asset to withdraw (ETH or USDC)')
|
|
24
18
|
.argument('<amount>', 'Amount to withdraw (e.g., 0.1)')
|
|
25
19
|
.argument('<recipient>', 'Recipient address (e.g., 0x...)')
|
|
26
|
-
.option('--
|
|
27
|
-
.
|
|
28
|
-
|
|
20
|
+
.option('--json', 'Output as JSON')
|
|
21
|
+
.addHelpText('after', `
|
|
22
|
+
Examples:
|
|
23
|
+
veil withdraw ETH 0.05 0xRecipientAddress
|
|
24
|
+
veil withdraw USDC 50 0xRecipientAddress
|
|
25
|
+
veil withdraw ETH 0.05 0xRecipientAddress --json
|
|
26
|
+
`)
|
|
29
27
|
.action(async (asset: string, amount: string, recipient: string, options) => {
|
|
30
28
|
try {
|
|
31
29
|
const assetUpper = asset.toUpperCase();
|
|
@@ -41,24 +39,16 @@ export function createWithdrawCommand(): Command {
|
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
// Get keypair
|
|
44
|
-
const veilKey =
|
|
42
|
+
const veilKey = process.env.VEIL_KEY;
|
|
45
43
|
if (!veilKey) {
|
|
46
|
-
throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required.
|
|
44
|
+
throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required. Set VEIL_KEY env');
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
const keypair = new Keypair(veilKey);
|
|
50
|
-
const rpcUrl =
|
|
48
|
+
const rpcUrl = process.env.RPC_URL;
|
|
51
49
|
const pool = assetUpper.toLowerCase() as RelayPool;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const onProgress = options.quiet
|
|
55
|
-
? undefined
|
|
56
|
-
: (stage: string, detail?: string) => {
|
|
57
|
-
const msg = detail ? `${stage}: ${detail}` : stage;
|
|
58
|
-
progress(msg, options.quiet);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
progress(`Starting ${assetUpper} withdrawal...`, options.quiet);
|
|
50
|
+
const onProgress = createProgressReporter();
|
|
51
|
+
onProgress(`Starting ${assetUpper} withdrawal...`);
|
|
62
52
|
|
|
63
53
|
// Execute withdrawal
|
|
64
54
|
const result = await withdraw({
|
|
@@ -70,21 +60,33 @@ export function createWithdrawCommand(): Command {
|
|
|
70
60
|
onProgress,
|
|
71
61
|
});
|
|
72
62
|
|
|
73
|
-
|
|
74
|
-
progress('', options.quiet);
|
|
63
|
+
clearProgress();
|
|
75
64
|
|
|
76
|
-
|
|
77
|
-
console.log(JSON.stringify({
|
|
65
|
+
const output = {
|
|
78
66
|
success: result.success,
|
|
79
67
|
transactionHash: result.transactionHash,
|
|
80
68
|
blockNumber: result.blockNumber,
|
|
81
69
|
asset: assetUpper,
|
|
82
70
|
amount: result.amount,
|
|
83
71
|
recipient: result.recipient,
|
|
84
|
-
}
|
|
85
|
-
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
if (options.json) {
|
|
75
|
+
printJson(output);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
printHeader('Withdrawal Submitted');
|
|
80
|
+
printFields([
|
|
81
|
+
{ label: 'Asset', value: assetUpper },
|
|
82
|
+
{ label: 'Amount', value: result.amount },
|
|
83
|
+
{ label: 'Recipient', value: result.recipient },
|
|
84
|
+
{ label: 'Transaction', value: txUrl(result.transactionHash) },
|
|
85
|
+
{ label: 'Block', value: result.blockNumber },
|
|
86
|
+
]);
|
|
87
|
+
printLine();
|
|
86
88
|
} catch (error) {
|
|
87
|
-
|
|
89
|
+
clearProgress();
|
|
88
90
|
handleCLIError(error);
|
|
89
91
|
}
|
|
90
92
|
});
|