@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.
@@ -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('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
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
- 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
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
- // Check VEIL_KEY
67
- const veilKey = process.env.VEIL_KEY;
68
- if (veilKey) {
69
- result.veilKey.found = true;
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
- // 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;
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
- // 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';
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
- // 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();
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.registration.error = error instanceof Error ? error.message : 'Unknown error';
175
+ result.relay.healthy = false;
176
+ result.relay.error = error instanceof Error ? error.message : 'Unknown error';
110
177
  }
111
- }
112
178
 
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;
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
- result.relay.healthy = false;
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', 'CBBTC'];
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, USDC, or CBBTC)')
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('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
27
- .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
28
- .option('--quiet', 'Suppress progress output')
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 = options.veilKey || process.env.VEIL_KEY;
42
+ const veilKey = process.env.VEIL_KEY;
45
43
  if (!veilKey) {
46
- throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required. Use --veil-key or set VEIL_KEY env');
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 = options.rpcUrl || process.env.RPC_URL;
48
+ const rpcUrl = process.env.RPC_URL;
51
49
  const pool = assetUpper.toLowerCase() as RelayPool;
52
-
53
- // Progress callback
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
- // Clear progress line
74
- progress('', options.quiet);
63
+ clearProgress();
75
64
 
76
- // Output result
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
- }, null, 2));
86
- process.exit(0);
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
- progress('', options.quiet);
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, USDC, or CBBTC)')
101
+ .argument('<asset>', 'Asset to merge (ETH or USDC)')
100
102
  .argument('<amount>', 'Amount to merge (e.g., 0.5)')
101
- .option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
102
- .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
103
- .option('--quiet', 'Suppress progress output')
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 = options.veilKey || process.env.VEIL_KEY;
120
+ const veilKey = process.env.VEIL_KEY;
115
121
  if (!veilKey) {
116
- throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required. Use --veil-key or set VEIL_KEY env');
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 = options.rpcUrl || process.env.RPC_URL;
126
+ const rpcUrl = process.env.RPC_URL;
121
127
  const pool = assetUpper.toLowerCase() as RelayPool;
122
-
123
- // Progress callback
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
- // Clear progress line
143
- progress('', options.quiet);
140
+ clearProgress();
144
141
 
145
- // Output result
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
- }, null, 2));
154
- process.exit(0);
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
- progress('', options.quiet);
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', 'CBBTC'];
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, USDC, or CBBTC)')
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('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
27
- .option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
28
- .option('--quiet', 'Suppress progress output')
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 = options.veilKey || process.env.VEIL_KEY;
42
+ const veilKey = process.env.VEIL_KEY;
45
43
  if (!veilKey) {
46
- throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'VEIL_KEY required. Use --veil-key or set VEIL_KEY env');
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 = options.rpcUrl || process.env.RPC_URL;
48
+ const rpcUrl = process.env.RPC_URL;
51
49
  const pool = assetUpper.toLowerCase() as RelayPool;
52
-
53
- // Progress callback
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
- // Clear progress line
74
- progress('', options.quiet);
63
+ clearProgress();
75
64
 
76
- // Output result
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
- }, null, 2));
85
- process.exit(0);
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
- progress('', options.quiet);
89
+ clearProgress();
88
90
  handleCLIError(error);
89
91
  }
90
92
  });