@veil-cash/sdk 0.2.0 → 0.3.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 +218 -41
- package/dist/cli/index.cjs +507 -130
- package/dist/index.cjs +214 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +172 -4
- package/dist/index.d.ts +172 -4
- package/dist/index.js +210 -32
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/abi.ts +30 -0
- package/src/addresses.ts +40 -1
- package/src/balance.ts +22 -15
- package/src/cli/commands/balance.ts +114 -54
- package/src/cli/commands/deposit.ts +76 -33
- package/src/cli/commands/init.ts +45 -8
- package/src/cli/commands/private-balance.ts +6 -1
- package/src/cli/commands/queue-balance.ts +5 -1
- package/src/cli/commands/register.ts +47 -12
- package/src/cli/commands/transfer.ts +23 -10
- package/src/cli/commands/withdraw.ts +13 -5
- package/src/cli/index.ts +1 -1
- package/src/deposit.ts +106 -1
- package/src/index.ts +8 -2
- package/src/keypair.ts +79 -0
- package/src/relay.ts +2 -2
- package/src/transfer.ts +14 -11
- package/src/types.ts +9 -2
- package/src/withdraw.ts +7 -6
package/src/cli/commands/init.ts
CHANGED
|
@@ -82,19 +82,56 @@ function saveVeilKeypair(veilKey: string, depositKey: string, envPath: string):
|
|
|
82
82
|
writeFileSync(envPath, content);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Resolve wallet key from CLI flag or WALLET_KEY env var
|
|
87
|
+
*/
|
|
88
|
+
function resolveWalletKey(options: { walletKey?: string }): `0x${string}` {
|
|
89
|
+
const raw = options.walletKey || process.env.WALLET_KEY;
|
|
90
|
+
if (!raw) {
|
|
91
|
+
throw new Error('Wallet key required for --sign-message. Use --wallet-key <key> or set WALLET_KEY env var.');
|
|
92
|
+
}
|
|
93
|
+
const key = raw.startsWith('0x') ? raw : `0x${raw}`;
|
|
94
|
+
if (key.length !== 66) {
|
|
95
|
+
throw new Error('Invalid wallet key format. Must be a 0x-prefixed 64-character hex string.');
|
|
96
|
+
}
|
|
97
|
+
return key as `0x${string}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
85
100
|
export function createInitCommand(): Command {
|
|
86
101
|
const init = new Command('init')
|
|
87
102
|
.description('Generate a new Veil keypair')
|
|
88
103
|
.option('--force', 'Overwrite existing keypair without prompting')
|
|
89
104
|
.option('--json', 'Output as JSON (no prompts, no file save)')
|
|
90
|
-
.option('--out <path>', 'Save to custom path instead of .env.veil')
|
|
91
105
|
.option('--no-save', 'Print keypair without saving to file')
|
|
106
|
+
.option('--sign-message', 'Derive keypair from wallet signature (same as frontend login)')
|
|
107
|
+
.option('--wallet-key <key>', 'Ethereum wallet private key (or set WALLET_KEY env var)')
|
|
108
|
+
.option('--signature <sig>', 'Derive keypair from a pre-computed EIP-191 personal_sign signature')
|
|
92
109
|
.action(async (options) => {
|
|
93
|
-
const envPath =
|
|
110
|
+
const envPath = getDefaultEnvPath();
|
|
94
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Create keypair: derived from wallet, from signature, or random
|
|
114
|
+
*/
|
|
115
|
+
async function createKp(): Promise<Keypair> {
|
|
116
|
+
if (options.signMessage) {
|
|
117
|
+
const walletKey = resolveWalletKey(options);
|
|
118
|
+
return Keypair.fromWalletKey(walletKey);
|
|
119
|
+
}
|
|
120
|
+
if (options.signature) {
|
|
121
|
+
return Keypair.fromSignature(options.signature);
|
|
122
|
+
}
|
|
123
|
+
return new Keypair();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const derivationLabel = options.signMessage
|
|
127
|
+
? 'Derived Veil keypair from wallet signature'
|
|
128
|
+
: options.signature
|
|
129
|
+
? 'Derived Veil keypair from provided signature'
|
|
130
|
+
: 'Generated new Veil keypair';
|
|
131
|
+
|
|
95
132
|
// JSON mode: no prompts, no save, just output JSON
|
|
96
133
|
if (options.json) {
|
|
97
|
-
const kp =
|
|
134
|
+
const kp = await createKp();
|
|
98
135
|
console.log(JSON.stringify({
|
|
99
136
|
veilKey: kp.privkey,
|
|
100
137
|
depositKey: kp.depositKey(),
|
|
@@ -105,13 +142,13 @@ export function createInitCommand(): Command {
|
|
|
105
142
|
|
|
106
143
|
// No-save mode: print but don't save
|
|
107
144
|
if (!options.save) {
|
|
108
|
-
const kp =
|
|
109
|
-
console.log(
|
|
145
|
+
const kp = await createKp();
|
|
146
|
+
console.log(`\n${derivationLabel}:\n`);
|
|
110
147
|
console.log('Veil Private Key:');
|
|
111
148
|
console.log(` ${kp.privkey}\n`);
|
|
112
149
|
console.log('Deposit Key (register this on-chain):');
|
|
113
150
|
console.log(` ${kp.depositKey()}\n`);
|
|
114
|
-
console.log('(Not saved -
|
|
151
|
+
console.log('(Not saved - run without --no-save to save to .env.veil)');
|
|
115
152
|
process.exit(0);
|
|
116
153
|
return;
|
|
117
154
|
}
|
|
@@ -129,9 +166,9 @@ export function createInitCommand(): Command {
|
|
|
129
166
|
}
|
|
130
167
|
}
|
|
131
168
|
|
|
132
|
-
const kp =
|
|
169
|
+
const kp = await createKp();
|
|
133
170
|
|
|
134
|
-
console.log(
|
|
171
|
+
console.log(`\n${derivationLabel}:\n`);
|
|
135
172
|
console.log('Veil Private Key:');
|
|
136
173
|
console.log(` ${kp.privkey}\n`);
|
|
137
174
|
console.log('Deposit Key (register this on-chain):');
|
|
@@ -5,16 +5,20 @@
|
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
import { getPrivateBalance } from '../../balance.js';
|
|
7
7
|
import { Keypair } from '../../keypair.js';
|
|
8
|
+
import type { RelayPool } from '../../types.js';
|
|
8
9
|
|
|
9
10
|
export function createPrivateBalanceCommand(): Command {
|
|
10
11
|
const privateBalance = new Command('private-balance')
|
|
11
12
|
.description('Show private balance (requires VEIL_KEY)')
|
|
13
|
+
.option('--pool <pool>', 'Pool to check (eth, usdc, or cbbtc)', 'eth')
|
|
12
14
|
.option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
|
|
13
15
|
.option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
|
|
14
16
|
.option('--show-utxos', 'Show individual UTXO details')
|
|
15
17
|
.option('--quiet', 'Suppress progress output')
|
|
16
18
|
.action(async (options) => {
|
|
17
19
|
try {
|
|
20
|
+
const pool = (options.pool || 'eth').toLowerCase() as RelayPool;
|
|
21
|
+
|
|
18
22
|
// Get keypair
|
|
19
23
|
const veilKey = options.veilKey || process.env.VEIL_KEY;
|
|
20
24
|
if (!veilKey) {
|
|
@@ -33,7 +37,7 @@ export function createPrivateBalanceCommand(): Command {
|
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
// Get private balance from SDK
|
|
36
|
-
const result = await getPrivateBalance({ keypair, rpcUrl, onProgress });
|
|
40
|
+
const result = await getPrivateBalance({ keypair, pool, rpcUrl, onProgress });
|
|
37
41
|
|
|
38
42
|
// Clear progress line
|
|
39
43
|
if (!options.quiet) {
|
|
@@ -42,6 +46,7 @@ export function createPrivateBalanceCommand(): Command {
|
|
|
42
46
|
|
|
43
47
|
// Format output
|
|
44
48
|
const output: Record<string, unknown> = {
|
|
49
|
+
pool: pool.toUpperCase(),
|
|
45
50
|
privateBalance: result.privateBalance,
|
|
46
51
|
privateBalanceWei: result.privateBalanceWei,
|
|
47
52
|
utxoCount: result.utxoCount,
|
|
@@ -5,16 +5,20 @@
|
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
import { getQueueBalance } from '../../balance.js';
|
|
7
7
|
import { getAddress } from '../wallet.js';
|
|
8
|
+
import type { RelayPool } from '../../types.js';
|
|
8
9
|
|
|
9
10
|
export function createQueueBalanceCommand(): Command {
|
|
10
11
|
const balance = new Command('queue-balance')
|
|
11
12
|
.description('Show queue balance and pending deposits')
|
|
13
|
+
.option('--pool <pool>', 'Pool to check (eth, usdc, or cbbtc)', 'eth')
|
|
12
14
|
.option('--wallet-key <key>', 'Ethereum wallet key (or set WALLET_KEY env)')
|
|
13
15
|
.option('--address <address>', 'Address to check (or derived from wallet key)')
|
|
14
16
|
.option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
|
|
15
17
|
.option('--quiet', 'Suppress progress output')
|
|
16
18
|
.action(async (options) => {
|
|
17
19
|
try {
|
|
20
|
+
const pool = (options.pool || 'eth').toLowerCase() as RelayPool;
|
|
21
|
+
|
|
18
22
|
// Get address
|
|
19
23
|
let address: `0x${string}`;
|
|
20
24
|
if (options.address) {
|
|
@@ -37,7 +41,7 @@ export function createQueueBalanceCommand(): Command {
|
|
|
37
41
|
|
|
38
42
|
// Get queue balance from SDK
|
|
39
43
|
const rpcUrl = options.rpcUrl || process.env.RPC_URL;
|
|
40
|
-
const result = await getQueueBalance({ address, rpcUrl, onProgress });
|
|
44
|
+
const result = await getQueueBalance({ address, pool, rpcUrl, onProgress });
|
|
41
45
|
|
|
42
46
|
// Clear progress line
|
|
43
47
|
if (!options.quiet) {
|
|
@@ -3,19 +3,20 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { Command } from 'commander';
|
|
6
|
-
import { buildRegisterTx } from '../../deposit.js';
|
|
6
|
+
import { buildRegisterTx, buildChangeDepositKeyTx } from '../../deposit.js';
|
|
7
7
|
import { sendTransaction, getAddress, isRegistered } from '../wallet.js';
|
|
8
8
|
import { getConfig } from '../config.js';
|
|
9
9
|
import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
|
|
10
10
|
|
|
11
11
|
export function createRegisterCommand(): Command {
|
|
12
12
|
const register = new Command('register')
|
|
13
|
-
.description('Register your deposit key on-chain
|
|
13
|
+
.description('Register or update your deposit key on-chain')
|
|
14
14
|
.option('--deposit-key <key>', 'Your Veil deposit key (or set DEPOSIT_KEY env)')
|
|
15
15
|
.option('--wallet-key <key>', 'Ethereum wallet key for signing (or set WALLET_KEY env)')
|
|
16
16
|
.option('--address <address>', 'Owner address (required with --unsigned)')
|
|
17
17
|
.option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
|
|
18
18
|
.option('--unsigned', 'Output unsigned transaction payload (Bankr-compatible format)')
|
|
19
|
+
.option('--force', 'Change deposit key even if already registered')
|
|
19
20
|
.option('--json', 'Output as JSON')
|
|
20
21
|
.action(async (options) => {
|
|
21
22
|
const jsonOutput = options.json;
|
|
@@ -40,9 +41,13 @@ export function createRegisterCommand(): Command {
|
|
|
40
41
|
address = getAddress(walletKey as `0x${string}`);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
// Use changeDepositKey if --force is set, otherwise register
|
|
45
|
+
const tx = options.force
|
|
46
|
+
? buildChangeDepositKeyTx(depositKey, address)
|
|
47
|
+
: buildRegisterTx(depositKey, address);
|
|
44
48
|
|
|
45
49
|
const payload = {
|
|
50
|
+
action: options.force ? 'changeDepositKey' : 'register',
|
|
46
51
|
to: tx.to,
|
|
47
52
|
data: tx.data,
|
|
48
53
|
value: '0',
|
|
@@ -62,45 +67,75 @@ export function createRegisterCommand(): Command {
|
|
|
62
67
|
if (!jsonOutput) console.log('\nChecking registration status...');
|
|
63
68
|
const { registered, depositKey: existingKey } = await isRegistered(address, config.rpcUrl);
|
|
64
69
|
|
|
65
|
-
if
|
|
70
|
+
// Determine if this is a new registration or a key change
|
|
71
|
+
const keysMatch = registered && existingKey === depositKey;
|
|
72
|
+
const isChange = registered && !keysMatch;
|
|
73
|
+
|
|
74
|
+
if (registered && keysMatch) {
|
|
75
|
+
// Already registered with the same key -- nothing to do
|
|
66
76
|
if (jsonOutput) {
|
|
67
77
|
console.log(JSON.stringify({
|
|
68
78
|
success: true,
|
|
69
79
|
alreadyRegistered: true,
|
|
80
|
+
keysMatch: true,
|
|
70
81
|
address,
|
|
71
82
|
depositKey: existingKey,
|
|
72
83
|
}, null, 2));
|
|
73
84
|
} else {
|
|
74
|
-
console.log(`\nAddress ${address} is already registered.`);
|
|
75
|
-
console.log(`\
|
|
76
|
-
console.log(` ${existingKey}`);
|
|
85
|
+
console.log(`\nAddress ${address} is already registered with this deposit key.`);
|
|
86
|
+
console.log(`\nDeposit key: ${existingKey!.slice(0, 40)}...`);
|
|
77
87
|
}
|
|
78
88
|
process.exit(0);
|
|
79
89
|
return;
|
|
80
90
|
}
|
|
81
91
|
|
|
92
|
+
if (isChange && !options.force) {
|
|
93
|
+
// Registered with a different key but --force not provided
|
|
94
|
+
if (jsonOutput) {
|
|
95
|
+
console.log(JSON.stringify({
|
|
96
|
+
success: false,
|
|
97
|
+
alreadyRegistered: true,
|
|
98
|
+
keysMatch: false,
|
|
99
|
+
address,
|
|
100
|
+
onChainKey: existingKey,
|
|
101
|
+
localKey: depositKey.slice(0, 40) + '...',
|
|
102
|
+
hint: 'Use --force to change deposit key',
|
|
103
|
+
}, null, 2));
|
|
104
|
+
} else {
|
|
105
|
+
console.log(`\nAddress ${address} is already registered with a different deposit key.`);
|
|
106
|
+
console.log(`\n On-chain key: ${existingKey!.slice(0, 40)}...`);
|
|
107
|
+
console.log(` Local key: ${depositKey.slice(0, 40)}...`);
|
|
108
|
+
console.log(`\nUse --force to change your deposit key on-chain.`);
|
|
109
|
+
}
|
|
110
|
+
process.exit(1);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Build the appropriate transaction
|
|
115
|
+
const action = isChange ? 'Changing deposit key' : 'Registering deposit key';
|
|
82
116
|
if (!jsonOutput) {
|
|
83
|
-
console.log(
|
|
117
|
+
console.log(`${action}...`);
|
|
84
118
|
console.log(` Address: ${address}`);
|
|
85
119
|
console.log(` Deposit Key: ${depositKey.slice(0, 40)}...`);
|
|
86
120
|
}
|
|
87
121
|
|
|
88
|
-
|
|
89
|
-
|
|
122
|
+
const tx = isChange
|
|
123
|
+
? buildChangeDepositKeyTx(depositKey, address)
|
|
124
|
+
: buildRegisterTx(depositKey, address);
|
|
90
125
|
const result = await sendTransaction(config, tx);
|
|
91
126
|
|
|
92
127
|
if (result.receipt.status === 'success') {
|
|
93
128
|
if (jsonOutput) {
|
|
94
129
|
console.log(JSON.stringify({
|
|
95
130
|
success: true,
|
|
96
|
-
|
|
131
|
+
action: isChange ? 'changed' : 'registered',
|
|
97
132
|
address,
|
|
98
133
|
transactionHash: result.hash,
|
|
99
134
|
blockNumber: result.receipt.blockNumber.toString(),
|
|
100
135
|
gasUsed: result.receipt.gasUsed.toString(),
|
|
101
136
|
}, null, 2));
|
|
102
137
|
} else {
|
|
103
|
-
console.log('\nRegistration successful!');
|
|
138
|
+
console.log(isChange ? '\nDeposit key changed successfully!' : '\nRegistration successful!');
|
|
104
139
|
console.log(` Transaction: ${result.hash}`);
|
|
105
140
|
console.log(` Block: ${result.receipt.blockNumber}`);
|
|
106
141
|
console.log(` Gas used: ${result.receipt.gasUsed}`);
|
|
@@ -6,6 +6,9 @@ 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 type { RelayPool } from '../../types.js';
|
|
10
|
+
|
|
11
|
+
const SUPPORTED_ASSETS = ['ETH', 'USDC', 'CBBTC'];
|
|
9
12
|
|
|
10
13
|
// Progress helper - writes to stderr so JSON output stays clean
|
|
11
14
|
function progress(msg: string, quiet?: boolean) {
|
|
@@ -17,7 +20,7 @@ function progress(msg: string, quiet?: boolean) {
|
|
|
17
20
|
export function createTransferCommand(): Command {
|
|
18
21
|
const transferCmd = new Command('transfer')
|
|
19
22
|
.description('Transfer privately within the pool to another registered address')
|
|
20
|
-
.argument('<asset>', 'Asset to transfer (ETH)')
|
|
23
|
+
.argument('<asset>', 'Asset to transfer (ETH, USDC, or CBBTC)')
|
|
21
24
|
.argument('<amount>', 'Amount to transfer (e.g., 0.1)')
|
|
22
25
|
.argument('<recipient>', 'Recipient address (must be registered)')
|
|
23
26
|
.option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
|
|
@@ -25,9 +28,11 @@ export function createTransferCommand(): Command {
|
|
|
25
28
|
.option('--quiet', 'Suppress progress output')
|
|
26
29
|
.action(async (asset: string, amount: string, recipient: string, options) => {
|
|
27
30
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
const assetUpper = asset.toUpperCase();
|
|
32
|
+
|
|
33
|
+
// Validate asset
|
|
34
|
+
if (!SUPPORTED_ASSETS.includes(assetUpper)) {
|
|
35
|
+
throw new CLIError(ErrorCode.INVALID_AMOUNT, `Unsupported asset: ${asset}. Supported: ${SUPPORTED_ASSETS.join(', ')}`);
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
// Validate recipient
|
|
@@ -43,6 +48,7 @@ export function createTransferCommand(): Command {
|
|
|
43
48
|
|
|
44
49
|
const senderKeypair = new Keypair(veilKey);
|
|
45
50
|
const rpcUrl = options.rpcUrl || process.env.RPC_URL;
|
|
51
|
+
const pool = assetUpper.toLowerCase() as RelayPool;
|
|
46
52
|
|
|
47
53
|
// Progress callback
|
|
48
54
|
const onProgress = options.quiet
|
|
@@ -52,13 +58,14 @@ export function createTransferCommand(): Command {
|
|
|
52
58
|
progress(msg, options.quiet);
|
|
53
59
|
};
|
|
54
60
|
|
|
55
|
-
progress(
|
|
61
|
+
progress(`Starting ${assetUpper} transfer...`, options.quiet);
|
|
56
62
|
|
|
57
63
|
// Execute transfer
|
|
58
64
|
const result = await transfer({
|
|
59
65
|
amount,
|
|
60
66
|
recipientAddress: recipient as `0x${string}`,
|
|
61
67
|
senderKeypair,
|
|
68
|
+
pool,
|
|
62
69
|
rpcUrl,
|
|
63
70
|
onProgress,
|
|
64
71
|
});
|
|
@@ -71,6 +78,7 @@ export function createTransferCommand(): Command {
|
|
|
71
78
|
success: result.success,
|
|
72
79
|
transactionHash: result.transactionHash,
|
|
73
80
|
blockNumber: result.blockNumber,
|
|
81
|
+
asset: assetUpper,
|
|
74
82
|
amount: result.amount,
|
|
75
83
|
recipient: result.recipient,
|
|
76
84
|
type: 'transfer',
|
|
@@ -88,16 +96,18 @@ export function createTransferCommand(): Command {
|
|
|
88
96
|
export function createMergeCommand(): Command {
|
|
89
97
|
const mergeCmd = new Command('merge')
|
|
90
98
|
.description('Merge UTXOs by self-transfer (consolidate small UTXOs)')
|
|
91
|
-
.argument('<asset>', 'Asset to merge (ETH)')
|
|
99
|
+
.argument('<asset>', 'Asset to merge (ETH, USDC, or CBBTC)')
|
|
92
100
|
.argument('<amount>', 'Amount to merge (e.g., 0.5)')
|
|
93
101
|
.option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
|
|
94
102
|
.option('--rpc-url <url>', 'RPC URL (or set RPC_URL env)')
|
|
95
103
|
.option('--quiet', 'Suppress progress output')
|
|
96
104
|
.action(async (asset: string, amount: string, options) => {
|
|
97
105
|
try {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
106
|
+
const assetUpper = asset.toUpperCase();
|
|
107
|
+
|
|
108
|
+
// Validate asset
|
|
109
|
+
if (!SUPPORTED_ASSETS.includes(assetUpper)) {
|
|
110
|
+
throw new CLIError(ErrorCode.INVALID_AMOUNT, `Unsupported asset: ${asset}. Supported: ${SUPPORTED_ASSETS.join(', ')}`);
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
// Get keypair
|
|
@@ -108,6 +118,7 @@ export function createMergeCommand(): Command {
|
|
|
108
118
|
|
|
109
119
|
const keypair = new Keypair(veilKey);
|
|
110
120
|
const rpcUrl = options.rpcUrl || process.env.RPC_URL;
|
|
121
|
+
const pool = assetUpper.toLowerCase() as RelayPool;
|
|
111
122
|
|
|
112
123
|
// Progress callback
|
|
113
124
|
const onProgress = options.quiet
|
|
@@ -117,12 +128,13 @@ export function createMergeCommand(): Command {
|
|
|
117
128
|
progress(msg, options.quiet);
|
|
118
129
|
};
|
|
119
130
|
|
|
120
|
-
progress(
|
|
131
|
+
progress(`Starting ${assetUpper} merge (self-transfer)...`, options.quiet);
|
|
121
132
|
|
|
122
133
|
// Execute merge
|
|
123
134
|
const result = await mergeUtxos({
|
|
124
135
|
amount,
|
|
125
136
|
keypair,
|
|
137
|
+
pool,
|
|
126
138
|
rpcUrl,
|
|
127
139
|
onProgress,
|
|
128
140
|
});
|
|
@@ -135,6 +147,7 @@ export function createMergeCommand(): Command {
|
|
|
135
147
|
success: result.success,
|
|
136
148
|
transactionHash: result.transactionHash,
|
|
137
149
|
blockNumber: result.blockNumber,
|
|
150
|
+
asset: assetUpper,
|
|
138
151
|
amount: result.amount,
|
|
139
152
|
type: 'merge',
|
|
140
153
|
}, null, 2));
|
|
@@ -6,6 +6,9 @@ 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 type { RelayPool } from '../../types.js';
|
|
10
|
+
|
|
11
|
+
const SUPPORTED_ASSETS = ['ETH', 'USDC', 'CBBTC'];
|
|
9
12
|
|
|
10
13
|
// Progress helper - writes to stderr so JSON output stays clean
|
|
11
14
|
function progress(msg: string, quiet?: boolean) {
|
|
@@ -17,7 +20,7 @@ function progress(msg: string, quiet?: boolean) {
|
|
|
17
20
|
export function createWithdrawCommand(): Command {
|
|
18
21
|
const withdrawCmd = new Command('withdraw')
|
|
19
22
|
.description('Withdraw from private pool to a public address')
|
|
20
|
-
.argument('<asset>', 'Asset to withdraw (ETH)')
|
|
23
|
+
.argument('<asset>', 'Asset to withdraw (ETH, USDC, or CBBTC)')
|
|
21
24
|
.argument('<amount>', 'Amount to withdraw (e.g., 0.1)')
|
|
22
25
|
.argument('<recipient>', 'Recipient address (e.g., 0x...)')
|
|
23
26
|
.option('--veil-key <key>', 'Veil private key (or set VEIL_KEY env)')
|
|
@@ -25,9 +28,11 @@ export function createWithdrawCommand(): Command {
|
|
|
25
28
|
.option('--quiet', 'Suppress progress output')
|
|
26
29
|
.action(async (asset: string, amount: string, recipient: string, options) => {
|
|
27
30
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
const assetUpper = asset.toUpperCase();
|
|
32
|
+
|
|
33
|
+
// Validate asset
|
|
34
|
+
if (!SUPPORTED_ASSETS.includes(assetUpper)) {
|
|
35
|
+
throw new CLIError(ErrorCode.INVALID_AMOUNT, `Unsupported asset: ${asset}. Supported: ${SUPPORTED_ASSETS.join(', ')}`);
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
// Validate recipient
|
|
@@ -43,6 +48,7 @@ export function createWithdrawCommand(): Command {
|
|
|
43
48
|
|
|
44
49
|
const keypair = new Keypair(veilKey);
|
|
45
50
|
const rpcUrl = options.rpcUrl || process.env.RPC_URL;
|
|
51
|
+
const pool = assetUpper.toLowerCase() as RelayPool;
|
|
46
52
|
|
|
47
53
|
// Progress callback
|
|
48
54
|
const onProgress = options.quiet
|
|
@@ -52,13 +58,14 @@ export function createWithdrawCommand(): Command {
|
|
|
52
58
|
progress(msg, options.quiet);
|
|
53
59
|
};
|
|
54
60
|
|
|
55
|
-
progress(
|
|
61
|
+
progress(`Starting ${assetUpper} withdrawal...`, options.quiet);
|
|
56
62
|
|
|
57
63
|
// Execute withdrawal
|
|
58
64
|
const result = await withdraw({
|
|
59
65
|
amount,
|
|
60
66
|
recipient: recipient as `0x${string}`,
|
|
61
67
|
keypair,
|
|
68
|
+
pool,
|
|
62
69
|
rpcUrl,
|
|
63
70
|
onProgress,
|
|
64
71
|
});
|
|
@@ -71,6 +78,7 @@ export function createWithdrawCommand(): Command {
|
|
|
71
78
|
success: result.success,
|
|
72
79
|
transactionHash: result.transactionHash,
|
|
73
80
|
blockNumber: result.blockNumber,
|
|
81
|
+
asset: assetUpper,
|
|
74
82
|
amount: result.amount,
|
|
75
83
|
recipient: result.recipient,
|
|
76
84
|
}, null, 2));
|
package/src/cli/index.ts
CHANGED
package/src/deposit.ts
CHANGED
|
@@ -47,6 +47,42 @@ export function buildRegisterTx(
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Build a transaction to change an existing deposit key
|
|
52
|
+
* The caller must already be registered on-chain
|
|
53
|
+
*
|
|
54
|
+
* @param depositKey - New deposit key from Keypair.depositKey()
|
|
55
|
+
* @param ownerAddress - Address that owns the current deposit key (must be msg.sender)
|
|
56
|
+
* @returns Transaction data to send
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const newKeypair = await Keypair.fromWalletKey('0x...');
|
|
61
|
+
* const tx = buildChangeDepositKeyTx(newKeypair.depositKey(), '0x...');
|
|
62
|
+
* // Send tx using your wallet
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function buildChangeDepositKeyTx(
|
|
66
|
+
depositKey: string,
|
|
67
|
+
ownerAddress: `0x${string}`
|
|
68
|
+
): TransactionData {
|
|
69
|
+
const addresses = getAddresses();
|
|
70
|
+
|
|
71
|
+
const data = encodeFunctionData({
|
|
72
|
+
abi: ENTRY_ABI,
|
|
73
|
+
functionName: 'changeDepositKey',
|
|
74
|
+
args: [{
|
|
75
|
+
owner: ownerAddress,
|
|
76
|
+
depositKey: depositKey as `0x${string}`,
|
|
77
|
+
}],
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
to: addresses.entry,
|
|
82
|
+
data,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
50
86
|
/**
|
|
51
87
|
* Build a transaction to deposit ETH
|
|
52
88
|
*
|
|
@@ -145,7 +181,65 @@ export function buildDepositUSDCTx(options: {
|
|
|
145
181
|
}
|
|
146
182
|
|
|
147
183
|
/**
|
|
148
|
-
* Build a
|
|
184
|
+
* Build a transaction to approve cbBTC for deposit
|
|
185
|
+
* Must be called before depositCBBTC if allowance is insufficient
|
|
186
|
+
*
|
|
187
|
+
* @param options - Approval options
|
|
188
|
+
* @param options.amount - Amount to approve (human readable, e.g., '0.5')
|
|
189
|
+
* @returns Transaction data
|
|
190
|
+
*/
|
|
191
|
+
export function buildApproveCBBTCTx(options: {
|
|
192
|
+
amount: string;
|
|
193
|
+
}): TransactionData {
|
|
194
|
+
const { amount } = options;
|
|
195
|
+
const addresses = getAddresses();
|
|
196
|
+
|
|
197
|
+
const amountWei = parseUnits(amount, POOL_CONFIG.cbbtc.decimals);
|
|
198
|
+
|
|
199
|
+
const data = encodeFunctionData({
|
|
200
|
+
abi: ERC20_ABI,
|
|
201
|
+
functionName: 'approve',
|
|
202
|
+
args: [addresses.entry, amountWei],
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
to: addresses.cbbtcToken,
|
|
207
|
+
data,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Build a transaction to deposit cbBTC
|
|
213
|
+
* Note: You must approve cbBTC first using buildApproveCBBTCTx
|
|
214
|
+
*
|
|
215
|
+
* @param options - Deposit options
|
|
216
|
+
* @param options.depositKey - Deposit key from Keypair.depositKey()
|
|
217
|
+
* @param options.amount - Amount to deposit (human readable, e.g., '0.5')
|
|
218
|
+
* @returns Transaction data
|
|
219
|
+
*/
|
|
220
|
+
export function buildDepositCBBTCTx(options: {
|
|
221
|
+
depositKey: string;
|
|
222
|
+
amount: string;
|
|
223
|
+
}): TransactionData {
|
|
224
|
+
const { depositKey, amount } = options;
|
|
225
|
+
const addresses = getAddresses();
|
|
226
|
+
|
|
227
|
+
const amountWei = parseUnits(amount, POOL_CONFIG.cbbtc.decimals);
|
|
228
|
+
|
|
229
|
+
const data = encodeFunctionData({
|
|
230
|
+
abi: ENTRY_ABI,
|
|
231
|
+
functionName: 'queueBTC',
|
|
232
|
+
args: [amountWei, depositKey as `0x${string}`],
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
to: addresses.entry,
|
|
237
|
+
data,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Build a deposit transaction (ETH, USDC, or cbBTC)
|
|
149
243
|
* Convenience function that routes to the correct builder
|
|
150
244
|
*
|
|
151
245
|
* @param options - Deposit options
|
|
@@ -166,6 +260,13 @@ export function buildDepositUSDCTx(options: {
|
|
|
166
260
|
* amount: '100',
|
|
167
261
|
* token: 'USDC',
|
|
168
262
|
* });
|
|
263
|
+
*
|
|
264
|
+
* // cbBTC deposit (remember to approve first!)
|
|
265
|
+
* const cbbtcTx = buildDepositTx({
|
|
266
|
+
* depositKey: keypair.depositKey(),
|
|
267
|
+
* amount: '0.5',
|
|
268
|
+
* token: 'CBBTC',
|
|
269
|
+
* });
|
|
169
270
|
* ```
|
|
170
271
|
*/
|
|
171
272
|
export function buildDepositTx(options: {
|
|
@@ -178,6 +279,10 @@ export function buildDepositTx(options: {
|
|
|
178
279
|
if (token === 'USDC') {
|
|
179
280
|
return buildDepositUSDCTx(rest);
|
|
180
281
|
}
|
|
282
|
+
|
|
283
|
+
if (token === 'CBBTC') {
|
|
284
|
+
return buildDepositCBBTCTx(rest);
|
|
285
|
+
}
|
|
181
286
|
|
|
182
287
|
return buildDepositETHTx(rest);
|
|
183
288
|
}
|
package/src/index.ts
CHANGED
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
// Keypair
|
|
35
|
-
export { Keypair, packEncryptedMessage, unpackEncryptedMessage } from './keypair.js';
|
|
35
|
+
export { Keypair, packEncryptedMessage, unpackEncryptedMessage, VEIL_SIGNED_MESSAGE } from './keypair.js';
|
|
36
|
+
export type { MessageSigner } from './keypair.js';
|
|
36
37
|
|
|
37
38
|
// UTXO
|
|
38
39
|
export { Utxo } from './utxo.js';
|
|
@@ -41,9 +42,12 @@ export type { UtxoParams } from './utxo.js';
|
|
|
41
42
|
// Deposit functions
|
|
42
43
|
export {
|
|
43
44
|
buildRegisterTx,
|
|
45
|
+
buildChangeDepositKeyTx,
|
|
44
46
|
buildDepositETHTx,
|
|
45
47
|
buildDepositUSDCTx,
|
|
46
48
|
buildApproveUSDCTx,
|
|
49
|
+
buildDepositCBBTCTx,
|
|
50
|
+
buildApproveCBBTCTx,
|
|
47
51
|
buildDepositTx,
|
|
48
52
|
} from './deposit.js';
|
|
49
53
|
|
|
@@ -99,7 +103,9 @@ export type { ProofInput } from './prover.js';
|
|
|
99
103
|
export {
|
|
100
104
|
ADDRESSES,
|
|
101
105
|
POOL_CONFIG,
|
|
102
|
-
getAddresses,
|
|
106
|
+
getAddresses,
|
|
107
|
+
getPoolAddress,
|
|
108
|
+
getQueueAddress,
|
|
103
109
|
getRelayUrl,
|
|
104
110
|
} from './addresses.js';
|
|
105
111
|
|