@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,95 +3,121 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Command } from 'commander';
|
|
6
|
-
import { buildDepositETHTx, buildDepositUSDCTx, buildApproveUSDCTx
|
|
6
|
+
import { buildDepositETHTx, buildDepositUSDCTx, buildApproveUSDCTx } from '../../deposit.js';
|
|
7
7
|
import { sendTransaction, getAddress, getBalance } from '../wallet.js';
|
|
8
8
|
import { getConfig } from '../config.js';
|
|
9
|
-
import { parseEther, formatEther } from 'viem';
|
|
9
|
+
import { createPublicClient, http, parseEther, parseUnits, formatEther, formatUnits } from 'viem';
|
|
10
|
+
import { base } from 'viem/chains';
|
|
10
11
|
import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
|
|
12
|
+
import { clearProgress, createProgressReporter, printFields, printHeader, printJson, printLine, txUrl } from '../output.js';
|
|
13
|
+
import { POOL_CONFIG, getAddresses } from '../../addresses.js';
|
|
14
|
+
import { ENTRY_ABI, ERC20_ABI } from '../../abi.js';
|
|
11
15
|
import type { TransactionData } from '../../types.js';
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
const DEPOSIT_FEE_PERCENT = 0.3;
|
|
15
|
-
const MINIMUM_DEPOSITS: Record<string, number> = {
|
|
17
|
+
const MINIMUM_NET: Record<string, number> = {
|
|
16
18
|
ETH: 0.01,
|
|
17
19
|
USDC: 10,
|
|
18
|
-
CBBTC: 0.0001,
|
|
19
20
|
};
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Query the Entry contract for the exact gross amount (net + fee).
|
|
24
|
+
* This matches the contract's own fee math and avoids rounding mismatches.
|
|
25
|
+
*/
|
|
26
|
+
async function getGrossAmount(
|
|
27
|
+
netWei: bigint,
|
|
28
|
+
rpcUrl: string | undefined,
|
|
29
|
+
): Promise<{ grossWei: bigint; feeWei: bigint }> {
|
|
30
|
+
const publicClient = createPublicClient({
|
|
31
|
+
chain: base,
|
|
32
|
+
transport: http(rpcUrl),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const grossWei = await publicClient.readContract({
|
|
36
|
+
address: getAddresses().entry,
|
|
37
|
+
abi: ENTRY_ABI,
|
|
38
|
+
functionName: 'getDepositAmountWithFee',
|
|
39
|
+
args: [netWei],
|
|
40
|
+
}) as bigint;
|
|
41
|
+
|
|
42
|
+
return { grossWei, feeWei: grossWei - netWei };
|
|
24
43
|
}
|
|
25
44
|
|
|
26
|
-
const SUPPORTED_ASSETS = ['ETH', 'USDC'
|
|
27
|
-
|
|
28
|
-
// Progress helper - writes to stderr so JSON output stays clean
|
|
29
|
-
function progress(msg: string, quiet?: boolean) {
|
|
30
|
-
if (!quiet) {
|
|
31
|
-
process.stderr.write(`\r\x1b[K${msg}`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
45
|
+
const SUPPORTED_ASSETS = ['ETH', 'USDC'];
|
|
34
46
|
|
|
35
47
|
export function createDepositCommand(): Command {
|
|
36
48
|
const deposit = new Command('deposit')
|
|
37
|
-
.description('Deposit ETH
|
|
38
|
-
.argument('<asset>', 'Asset to deposit (ETH
|
|
39
|
-
.argument('<amount>', 'Amount to deposit
|
|
40
|
-
.option('--
|
|
41
|
-
.option('--
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
.description('Deposit ETH or USDC into Veil')
|
|
50
|
+
.argument('<asset>', 'Asset to deposit (ETH or USDC)')
|
|
51
|
+
.argument('<amount>', 'Amount to deposit — this is what arrives in your Veil balance')
|
|
52
|
+
.option('--unsigned', 'Output unsigned transaction payload instead of sending')
|
|
53
|
+
.option('--json', 'Output as JSON')
|
|
54
|
+
.addHelpText('after', `
|
|
55
|
+
The amount you specify is the net amount that lands in your Veil balance.
|
|
56
|
+
The 0.3% protocol fee is automatically added on top.
|
|
57
|
+
|
|
58
|
+
Examples:
|
|
59
|
+
veil deposit ETH 0.1 # deposits 0.1 ETH (sends ~0.1003 ETH)
|
|
60
|
+
veil deposit USDC 100 # deposits 100 USDC (sends ~100.30 USDC)
|
|
61
|
+
veil deposit ETH 0.1 --unsigned
|
|
62
|
+
veil deposit ETH 0.1 --json
|
|
63
|
+
`)
|
|
45
64
|
.action(async (asset: string, amount: string, options) => {
|
|
46
65
|
try {
|
|
47
66
|
const assetUpper = asset.toUpperCase();
|
|
48
67
|
|
|
49
|
-
// Validate asset
|
|
50
68
|
if (!SUPPORTED_ASSETS.includes(assetUpper)) {
|
|
51
69
|
throw new CLIError(ErrorCode.INVALID_AMOUNT, `Unsupported asset: ${asset}. Supported: ${SUPPORTED_ASSETS.join(', ')}`);
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
const amountNum = parseFloat(amount);
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// Check minimum deposit
|
|
59
|
-
if (amountNum < minimumWithFee) {
|
|
73
|
+
const minimumNet = MINIMUM_NET[assetUpper];
|
|
74
|
+
|
|
75
|
+
if (amountNum < minimumNet) {
|
|
60
76
|
throw new CLIError(
|
|
61
77
|
ErrorCode.INVALID_AMOUNT,
|
|
62
|
-
`Minimum deposit is ${minimumNet} ${assetUpper}
|
|
63
|
-
`With ${DEPOSIT_FEE_PERCENT}% fee, send at least ${minimumWithFee.toFixed(assetUpper === 'ETH' ? 5 : 8)} ${assetUpper}.`
|
|
78
|
+
`Minimum deposit is ${minimumNet} ${assetUpper}.`
|
|
64
79
|
);
|
|
65
80
|
}
|
|
66
81
|
|
|
67
|
-
|
|
68
|
-
const
|
|
82
|
+
const rpcUrl = process.env.RPC_URL;
|
|
83
|
+
const poolConfig = POOL_CONFIG[assetUpper.toLowerCase() as 'eth' | 'usdc'];
|
|
84
|
+
const netWei = assetUpper === 'ETH'
|
|
85
|
+
? parseEther(amount)
|
|
86
|
+
: parseUnits(amount, poolConfig.decimals);
|
|
87
|
+
|
|
88
|
+
const progress = createProgressReporter();
|
|
89
|
+
progress('Calculating fee...');
|
|
90
|
+
|
|
91
|
+
const { grossWei, feeWei } = await getGrossAmount(netWei, rpcUrl);
|
|
92
|
+
const grossStr = assetUpper === 'ETH'
|
|
93
|
+
? formatEther(grossWei)
|
|
94
|
+
: formatUnits(grossWei, poolConfig.decimals);
|
|
95
|
+
const feeStr = assetUpper === 'ETH'
|
|
96
|
+
? formatEther(feeWei)
|
|
97
|
+
: formatUnits(feeWei, poolConfig.decimals);
|
|
98
|
+
|
|
99
|
+
const depositKey = process.env.DEPOSIT_KEY;
|
|
69
100
|
if (!depositKey) {
|
|
70
|
-
throw new CLIError(ErrorCode.DEPOSIT_KEY_MISSING, '
|
|
101
|
+
throw new CLIError(ErrorCode.DEPOSIT_KEY_MISSING, 'DEPOSIT_KEY not set. Run "veil init" first.');
|
|
71
102
|
}
|
|
72
103
|
|
|
73
|
-
progress('Building transaction...'
|
|
104
|
+
progress('Building transaction...');
|
|
74
105
|
|
|
75
|
-
// Build the deposit transaction
|
|
76
106
|
let tx: TransactionData;
|
|
77
107
|
let approveTx: TransactionData | null = null;
|
|
78
108
|
|
|
79
109
|
if (assetUpper === 'USDC') {
|
|
80
|
-
approveTx = buildApproveUSDCTx({ amount });
|
|
81
|
-
tx = buildDepositUSDCTx({ depositKey, amount });
|
|
82
|
-
} else if (assetUpper === 'CBBTC') {
|
|
83
|
-
approveTx = buildApproveCBBTCTx({ amount });
|
|
84
|
-
tx = buildDepositCBBTCTx({ depositKey, amount });
|
|
110
|
+
approveTx = buildApproveUSDCTx({ amount: grossStr });
|
|
111
|
+
tx = buildDepositUSDCTx({ depositKey, amount: grossStr });
|
|
85
112
|
} else {
|
|
86
|
-
tx = buildDepositETHTx({ depositKey, amount });
|
|
113
|
+
tx = buildDepositETHTx({ depositKey, amount: grossStr });
|
|
87
114
|
}
|
|
88
115
|
|
|
89
|
-
//
|
|
116
|
+
// --unsigned mode
|
|
90
117
|
if (options.unsigned) {
|
|
91
|
-
|
|
118
|
+
clearProgress();
|
|
92
119
|
const payloads: Record<string, unknown>[] = [];
|
|
93
120
|
|
|
94
|
-
// Include approval tx for ERC20 tokens
|
|
95
121
|
if (approveTx) {
|
|
96
122
|
payloads.push({
|
|
97
123
|
step: 'approve',
|
|
@@ -110,52 +136,95 @@ export function createDepositCommand(): Command {
|
|
|
110
136
|
chainId: 8453,
|
|
111
137
|
});
|
|
112
138
|
|
|
113
|
-
|
|
139
|
+
printJson(payloads.length === 1 ? payloads[0] : payloads);
|
|
114
140
|
return;
|
|
115
141
|
}
|
|
116
142
|
|
|
117
|
-
// Regular mode: sign and send
|
|
118
143
|
const config = getConfig(options);
|
|
119
144
|
const address = getAddress(config.privateKey);
|
|
120
145
|
|
|
121
|
-
// For ETH deposits, check ETH balance
|
|
122
146
|
if (assetUpper === 'ETH') {
|
|
123
|
-
progress('Checking balance...'
|
|
147
|
+
progress('Checking balance...');
|
|
124
148
|
const balance = await getBalance(address, config.rpcUrl);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
149
|
+
|
|
150
|
+
if (balance < grossWei) {
|
|
151
|
+
clearProgress();
|
|
152
|
+
throw new CLIError(
|
|
153
|
+
ErrorCode.INSUFFICIENT_BALANCE,
|
|
154
|
+
`Insufficient ETH balance. Have: ${formatEther(balance)} ETH, Need: ${grossStr} ETH (${amount} + fee)`
|
|
155
|
+
);
|
|
130
156
|
}
|
|
131
157
|
}
|
|
132
158
|
|
|
133
|
-
// Send approval transaction for ERC20 tokens
|
|
134
159
|
if (approveTx) {
|
|
135
|
-
progress(`Approving ${assetUpper}
|
|
136
|
-
await sendTransaction(config, approveTx);
|
|
160
|
+
progress(`Approving ${assetUpper}...`);
|
|
161
|
+
const approvalResult = await sendTransaction(config, approveTx);
|
|
162
|
+
if (assetUpper === 'USDC') {
|
|
163
|
+
const publicClient = createPublicClient({
|
|
164
|
+
chain: base,
|
|
165
|
+
transport: http(config.rpcUrl),
|
|
166
|
+
});
|
|
167
|
+
const addresses = getAddresses();
|
|
168
|
+
let allowance = await publicClient.readContract({
|
|
169
|
+
address: getAddresses().usdcToken,
|
|
170
|
+
abi: ERC20_ABI,
|
|
171
|
+
functionName: 'allowance',
|
|
172
|
+
args: [address, addresses.entry],
|
|
173
|
+
}) as bigint;
|
|
174
|
+
for (let confirmations = 2; allowance < grossWei && confirmations <= 3; confirmations++) {
|
|
175
|
+
await publicClient.waitForTransactionReceipt({
|
|
176
|
+
hash: approvalResult.hash,
|
|
177
|
+
confirmations,
|
|
178
|
+
});
|
|
179
|
+
allowance = await publicClient.readContract({
|
|
180
|
+
address: addresses.usdcToken,
|
|
181
|
+
abi: ERC20_ABI,
|
|
182
|
+
functionName: 'allowance',
|
|
183
|
+
args: [address, addresses.entry],
|
|
184
|
+
}) as bigint;
|
|
185
|
+
}
|
|
186
|
+
if (allowance < grossWei) {
|
|
187
|
+
throw new CLIError(
|
|
188
|
+
ErrorCode.CONTRACT_ERROR,
|
|
189
|
+
`USDC approval is not yet visible on RPC after confirmation. Allowance ${allowance.toString()} < required ${grossWei.toString()}.`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
137
193
|
}
|
|
138
194
|
|
|
139
|
-
progress('Sending deposit transaction...'
|
|
140
|
-
|
|
141
|
-
// Send the deposit transaction
|
|
195
|
+
progress('Sending deposit transaction...');
|
|
142
196
|
const result = await sendTransaction(config, tx);
|
|
197
|
+
progress('Confirming...');
|
|
198
|
+
clearProgress();
|
|
143
199
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
// Clear progress line
|
|
147
|
-
progress('', options.quiet);
|
|
148
|
-
|
|
149
|
-
console.log(JSON.stringify({
|
|
200
|
+
const output = {
|
|
150
201
|
success: result.receipt.status === 'success',
|
|
151
202
|
hash: result.hash,
|
|
152
203
|
asset: assetUpper,
|
|
153
204
|
amount,
|
|
205
|
+
fee: feeStr,
|
|
206
|
+
totalSent: grossStr,
|
|
154
207
|
blockNumber: result.receipt.blockNumber.toString(),
|
|
155
|
-
|
|
156
|
-
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
if (options.json) {
|
|
211
|
+
printJson(output);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
printHeader('Deposit Submitted');
|
|
216
|
+
printFields([
|
|
217
|
+
{ label: 'Asset', value: assetUpper },
|
|
218
|
+
{ label: 'Amount', value: `${amount} ${assetUpper}` },
|
|
219
|
+
{ label: 'Fee', value: `${feeStr} ${assetUpper} (0.3%)` },
|
|
220
|
+
{ label: 'Total sent', value: `${grossStr} ${assetUpper}` },
|
|
221
|
+
{ label: 'From', value: address },
|
|
222
|
+
{ label: 'Transaction', value: txUrl(result.hash) },
|
|
223
|
+
{ label: 'Block', value: result.receipt.blockNumber },
|
|
224
|
+
]);
|
|
225
|
+
printLine();
|
|
157
226
|
} catch (error) {
|
|
158
|
-
|
|
227
|
+
clearProgress();
|
|
159
228
|
handleCLIError(error);
|
|
160
229
|
}
|
|
161
230
|
});
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
|
8
8
|
import { dirname } from 'path';
|
|
9
9
|
import { mkdirSync } from 'fs';
|
|
10
10
|
import { Keypair } from '../../keypair.js';
|
|
11
|
+
import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
|
|
12
|
+
import { printFields, printHeader, printJson, printLine } from '../output.js';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Prompt user for yes/no confirmation
|
|
@@ -33,6 +35,18 @@ function getDefaultEnvPath(): string {
|
|
|
33
35
|
return '.env.veil';
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Enforce mutually exclusive wallet env modes.
|
|
40
|
+
*/
|
|
41
|
+
function ensureAddressEnvConsistency(): void {
|
|
42
|
+
if (process.env.WALLET_KEY && process.env.SIGNER_ADDRESS) {
|
|
43
|
+
throw new CLIError(
|
|
44
|
+
ErrorCode.CONFIG_CONFLICT,
|
|
45
|
+
'WALLET_KEY and SIGNER_ADDRESS are mutually exclusive. Set only one.',
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
36
50
|
/**
|
|
37
51
|
* Check if VEIL_KEY exists in an env file
|
|
38
52
|
*/
|
|
@@ -62,7 +76,6 @@ function updateEnvVar(content: string, key: string, value: string): string {
|
|
|
62
76
|
* Save Veil keypair to a file
|
|
63
77
|
*/
|
|
64
78
|
function saveVeilKeypair(veilKey: string, depositKey: string, envPath: string): void {
|
|
65
|
-
// Ensure directory exists
|
|
66
79
|
const dir = dirname(envPath);
|
|
67
80
|
if (dir && dir !== '.' && !existsSync(dir)) {
|
|
68
81
|
mkdirSync(dir, { recursive: true });
|
|
@@ -85,99 +98,116 @@ function saveVeilKeypair(veilKey: string, depositKey: string, envPath: string):
|
|
|
85
98
|
/**
|
|
86
99
|
* Resolve wallet key from CLI flag or WALLET_KEY env var
|
|
87
100
|
*/
|
|
88
|
-
function resolveWalletKey(
|
|
89
|
-
const raw =
|
|
101
|
+
function resolveWalletKey(): `0x${string}` {
|
|
102
|
+
const raw = process.env.WALLET_KEY;
|
|
90
103
|
if (!raw) {
|
|
91
|
-
|
|
104
|
+
const hasExternalSigner = Boolean(process.env.SIGNER_ADDRESS);
|
|
105
|
+
throw new CLIError(
|
|
106
|
+
ErrorCode.WALLET_KEY_MISSING,
|
|
107
|
+
hasExternalSigner
|
|
108
|
+
? 'WALLET_KEY env var required for wallet-derived init. If you are using an external signer, use "veil init --signature 0x..." or use --generate for a random keypair.'
|
|
109
|
+
: 'WALLET_KEY env var required. Set it or use --generate for a random keypair.',
|
|
110
|
+
);
|
|
92
111
|
}
|
|
93
112
|
const key = raw.startsWith('0x') ? raw : `0x${raw}`;
|
|
94
113
|
if (key.length !== 66) {
|
|
95
|
-
throw new
|
|
114
|
+
throw new CLIError(ErrorCode.WALLET_KEY_MISSING, 'Invalid WALLET_KEY format. Must be a 0x-prefixed 64-character hex string.');
|
|
96
115
|
}
|
|
97
116
|
return key as `0x${string}`;
|
|
98
117
|
}
|
|
99
118
|
|
|
100
119
|
export function createInitCommand(): Command {
|
|
101
120
|
const init = new Command('init')
|
|
102
|
-
.description('
|
|
121
|
+
.description('Derive a Veil keypair from your wallet (or generate a random one)')
|
|
122
|
+
.option('--generate', 'Generate a random keypair instead of deriving from wallet')
|
|
123
|
+
.option('--signature <sig>', 'Derive keypair from a pre-computed EIP-191 personal_sign signature')
|
|
103
124
|
.option('--force', 'Overwrite existing keypair without prompting')
|
|
104
125
|
.option('--json', 'Output as JSON (no prompts, no file save)')
|
|
105
126
|
.option('--no-save', 'Print keypair without saving to file')
|
|
106
|
-
.
|
|
107
|
-
|
|
108
|
-
|
|
127
|
+
.addHelpText('after', `
|
|
128
|
+
Examples:
|
|
129
|
+
veil init Derive from WALLET_KEY (default)
|
|
130
|
+
veil init --generate Generate a random keypair
|
|
131
|
+
veil init --signature 0x... Derive from a pre-computed signature
|
|
132
|
+
veil init --json Output keypair as JSON
|
|
133
|
+
`)
|
|
109
134
|
.action(async (options) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
135
|
+
try {
|
|
136
|
+
ensureAddressEnvConsistency();
|
|
137
|
+
const envPath = getDefaultEnvPath();
|
|
138
|
+
const useRandom = options.generate;
|
|
139
|
+
const useSignature = options.signature;
|
|
140
|
+
|
|
141
|
+
async function createKp(): Promise<Keypair> {
|
|
142
|
+
if (useSignature) {
|
|
143
|
+
return Keypair.fromSignature(options.signature);
|
|
144
|
+
}
|
|
145
|
+
if (useRandom) {
|
|
146
|
+
return new Keypair();
|
|
147
|
+
}
|
|
148
|
+
const walletKey = resolveWalletKey();
|
|
118
149
|
return Keypair.fromWalletKey(walletKey);
|
|
119
150
|
}
|
|
120
|
-
if (options.signature) {
|
|
121
|
-
return Keypair.fromSignature(options.signature);
|
|
122
|
-
}
|
|
123
|
-
return new Keypair();
|
|
124
|
-
}
|
|
125
151
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
152
|
+
const derivation: 'wallet-signature' | 'provided-signature' | 'random' =
|
|
153
|
+
useSignature ? 'provided-signature'
|
|
154
|
+
: useRandom ? 'random'
|
|
155
|
+
: 'wallet-signature';
|
|
156
|
+
|
|
157
|
+
const derivationLabel =
|
|
158
|
+
useSignature ? 'Derived keypair from provided signature'
|
|
159
|
+
: useRandom ? 'Generated random keypair'
|
|
160
|
+
: 'Derived keypair from wallet signature';
|
|
131
161
|
|
|
132
|
-
// JSON mode: no prompts, no save, just output JSON
|
|
133
|
-
if (options.json) {
|
|
134
162
|
const kp = await createKp();
|
|
135
|
-
|
|
163
|
+
const result = {
|
|
136
164
|
veilKey: kp.privkey,
|
|
165
|
+
veilPrivateKey: kp.privkey,
|
|
137
166
|
depositKey: kp.depositKey(),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
167
|
+
derivation,
|
|
168
|
+
};
|
|
142
169
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
console.log('Veil Private Key:');
|
|
148
|
-
console.log(` ${kp.privkey}\n`);
|
|
149
|
-
console.log('Deposit Key (register this on-chain):');
|
|
150
|
-
console.log(` ${kp.depositKey()}\n`);
|
|
151
|
-
console.log('(Not saved - run without --no-save to save to .env.veil)');
|
|
152
|
-
process.exit(0);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
170
|
+
if (options.json) {
|
|
171
|
+
printJson(result);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
155
174
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
175
|
+
if (!options.save) {
|
|
176
|
+
printHeader(derivationLabel);
|
|
177
|
+
printFields([
|
|
178
|
+
{ label: 'Veil private key', value: kp.privkey },
|
|
179
|
+
{ label: 'Deposit key', value: kp.depositKey() },
|
|
180
|
+
{ label: 'Saved', value: 'no' },
|
|
181
|
+
]);
|
|
182
|
+
printLine();
|
|
183
|
+
printLine('Run `veil init` without `--no-save` to persist these keys to .env.veil.');
|
|
184
|
+
printLine();
|
|
165
185
|
return;
|
|
166
186
|
}
|
|
187
|
+
|
|
188
|
+
const keyExists = veilKeyExistsAt(envPath);
|
|
189
|
+
if (keyExists && !options.force) {
|
|
190
|
+
printLine(`WARNING: A Veil key already exists in ${envPath}`);
|
|
191
|
+
const proceed = await confirm('Create a new key? This will overwrite the existing one');
|
|
192
|
+
if (!proceed) {
|
|
193
|
+
printLine('Aborted. Existing key preserved.');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
saveVeilKeypair(kp.privkey!, kp.depositKey(), envPath);
|
|
199
|
+
printHeader(derivationLabel);
|
|
200
|
+
printFields([
|
|
201
|
+
{ label: 'Veil private key', value: kp.privkey },
|
|
202
|
+
{ label: 'Deposit key', value: kp.depositKey() },
|
|
203
|
+
{ label: 'Saved to', value: envPath },
|
|
204
|
+
]);
|
|
205
|
+
printLine();
|
|
206
|
+
printLine('Next step: veil register');
|
|
207
|
+
printLine();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
handleCLIError(error);
|
|
167
210
|
}
|
|
168
|
-
|
|
169
|
-
const kp = await createKp();
|
|
170
|
-
|
|
171
|
-
console.log(`\n${derivationLabel}:\n`);
|
|
172
|
-
console.log('Veil Private Key:');
|
|
173
|
-
console.log(` ${kp.privkey}\n`);
|
|
174
|
-
console.log('Deposit Key (register this on-chain):');
|
|
175
|
-
console.log(` ${kp.depositKey()}\n`);
|
|
176
|
-
|
|
177
|
-
saveVeilKeypair(kp.privkey!, kp.depositKey(), envPath);
|
|
178
|
-
console.log(`Saved to ${envPath}`);
|
|
179
|
-
console.log('\nNext step: veil register');
|
|
180
|
-
process.exit(0);
|
|
181
211
|
});
|
|
182
212
|
|
|
183
213
|
return init;
|
|
@@ -4,27 +4,45 @@
|
|
|
4
4
|
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
import { Keypair } from '../../keypair.js';
|
|
7
|
+
import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
|
|
8
|
+
import { printFields, printJson, printLine } from '../output.js';
|
|
7
9
|
|
|
8
10
|
export function createKeypairCommand(): Command {
|
|
9
11
|
const keypair = new Command('keypair')
|
|
10
|
-
.description('Show current Veil keypair
|
|
12
|
+
.description('Show your current Veil keypair')
|
|
13
|
+
.option('--json', 'Output as JSON')
|
|
14
|
+
.addHelpText('after', `
|
|
15
|
+
Examples:
|
|
16
|
+
veil keypair
|
|
17
|
+
veil keypair --json
|
|
18
|
+
`)
|
|
11
19
|
.action(() => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
try {
|
|
21
|
+
const veilKey = process.env.VEIL_KEY;
|
|
22
|
+
|
|
23
|
+
if (!veilKey) {
|
|
24
|
+
throw new CLIError(ErrorCode.VEIL_KEY_MISSING, 'No keypair found. Run "veil init" first.');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const kp = new Keypair(veilKey);
|
|
28
|
+
const result = {
|
|
29
|
+
veilPrivateKey: kp.privkey,
|
|
30
|
+
depositKey: kp.depositKey(),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if ((keypair.opts() as { json?: boolean }).json) {
|
|
34
|
+
printJson(result);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
printLine('Veil keypair');
|
|
39
|
+
printFields([
|
|
40
|
+
{ label: 'Veil private key', value: result.veilPrivateKey },
|
|
41
|
+
{ label: 'Deposit key', value: result.depositKey },
|
|
42
|
+
]);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
handleCLIError(error);
|
|
20
45
|
}
|
|
21
|
-
|
|
22
|
-
const kp = new Keypair(veilKey);
|
|
23
|
-
|
|
24
|
-
console.log(JSON.stringify({
|
|
25
|
-
veilPrivateKey: kp.privkey,
|
|
26
|
-
depositKey: kp.depositKey(),
|
|
27
|
-
}, null, 2));
|
|
28
46
|
});
|
|
29
47
|
|
|
30
48
|
return keypair;
|