@veil-cash/sdk 0.4.0 → 0.6.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 +130 -464
- package/SDK.md +365 -0
- package/dist/cli/index.cjs +2260 -955
- package/dist/index.cjs +561 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +486 -1
- package/dist/index.d.ts +486 -1
- package/dist/index.js +544 -27
- 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 +172 -0
- package/src/addresses.ts +20 -2
- package/src/balance.ts +4 -4
- package/src/cli/commands/balance.ts +126 -38
- package/src/cli/commands/deposit.ts +136 -63
- package/src/cli/commands/init.ts +95 -72
- package/src/cli/commands/keypair.ts +34 -16
- package/src/cli/commands/private-balance.ts +37 -35
- package/src/cli/commands/queue-balance.ts +48 -36
- package/src/cli/commands/register.ts +67 -53
- package/src/cli/commands/status.ts +196 -70
- package/src/cli/commands/subaccount.ts +354 -0
- package/src/cli/commands/transfer.ts +62 -53
- package/src/cli/commands/withdraw.ts +32 -30
- package/src/cli/config.ts +85 -5
- package/src/cli/errors.ts +8 -0
- package/src/cli/index.ts +27 -5
- package/src/cli/output.ts +87 -0
- package/src/cli/wallet.ts +75 -16
- package/src/index.ts +41 -1
- package/src/prover.ts +3 -0
- package/src/relay.ts +36 -24
- package/src/subaccount.ts +476 -0
- package/src/types.ts +134 -0
|
@@ -6,9 +6,13 @@ import { Command } from 'commander';
|
|
|
6
6
|
import { getQueueBalance, getPrivateBalance } from '../../balance.js';
|
|
7
7
|
import { POOL_CONFIG } from '../../addresses.js';
|
|
8
8
|
import { Keypair } from '../../keypair.js';
|
|
9
|
-
import {
|
|
9
|
+
import { getWalletBalances } from '../wallet.js';
|
|
10
|
+
import { resolveAddress } from '../config.js';
|
|
10
11
|
import { formatUnits } from 'viem';
|
|
11
12
|
import { handleCLIError, CLIError, ErrorCode } from '../errors.js';
|
|
13
|
+
import { clearProgress, createProgressReporter, maskValue, printFields, printHeader, printJson, printLine, printList, printSection } from '../output.js';
|
|
14
|
+
import { createPrivateBalanceCommand } from './private-balance.js';
|
|
15
|
+
import { createQueueBalanceCommand } from './queue-balance.js';
|
|
12
16
|
import type { RelayPool } from '../../types.js';
|
|
13
17
|
|
|
14
18
|
const SUPPORTED_POOLS: RelayPool[] = ['eth', 'usdc'];
|
|
@@ -89,11 +93,15 @@ export function createBalanceCommand(): Command {
|
|
|
89
93
|
const balance = new Command('balance')
|
|
90
94
|
.description('Show queue and private balances (all pools by default)')
|
|
91
95
|
.option('--pool <pool>', 'Pool to check (eth, usdc, or all)', 'all')
|
|
92
|
-
.option('--
|
|
93
|
-
.option('--
|
|
94
|
-
.
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
.option('--address <address>', 'Address to check (or derived from WALLET_KEY / SIGNER_ADDRESS)')
|
|
97
|
+
.option('--json', 'Output as JSON')
|
|
98
|
+
.addHelpText('after', `
|
|
99
|
+
Examples:
|
|
100
|
+
veil balance
|
|
101
|
+
veil balance --pool eth
|
|
102
|
+
veil balance queue --pool usdc
|
|
103
|
+
veil balance --json
|
|
104
|
+
`)
|
|
97
105
|
.action(async (options) => {
|
|
98
106
|
try {
|
|
99
107
|
const poolArg = (options.pool || 'all').toLowerCase();
|
|
@@ -106,73 +114,153 @@ export function createBalanceCommand(): Command {
|
|
|
106
114
|
const poolsToQuery: RelayPool[] = poolArg === 'all' ? [...SUPPORTED_POOLS] : [poolArg as RelayPool];
|
|
107
115
|
|
|
108
116
|
// Get address
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
throw new CLIError(ErrorCode.WALLET_KEY_MISSING, 'Must provide --address or --wallet-key (or set WALLET_KEY env)');
|
|
116
|
-
}
|
|
117
|
-
address = getAddress(walletKey as `0x${string}`);
|
|
117
|
+
const resolvedAddress = resolveAddress({ address: options.address }, { required: true });
|
|
118
|
+
if (!resolvedAddress) {
|
|
119
|
+
throw new CLIError(
|
|
120
|
+
ErrorCode.WALLET_KEY_MISSING,
|
|
121
|
+
'Must provide --address, set SIGNER_ADDRESS, or set WALLET_KEY env.',
|
|
122
|
+
);
|
|
118
123
|
}
|
|
124
|
+
const address = resolvedAddress.address;
|
|
119
125
|
|
|
120
126
|
// Get keypair for private balance
|
|
121
|
-
const veilKey =
|
|
127
|
+
const veilKey = process.env.VEIL_KEY;
|
|
122
128
|
const keypair = veilKey ? new Keypair(veilKey) : null;
|
|
123
129
|
|
|
124
|
-
const rpcUrl =
|
|
130
|
+
const rpcUrl = process.env.RPC_URL;
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
const onProgress = options.quiet
|
|
128
|
-
? undefined
|
|
129
|
-
: (stage: string, detail?: string) => {
|
|
130
|
-
const msg = detail ? `${stage}: ${detail}` : stage;
|
|
131
|
-
process.stderr.write(`\r\x1b[K${msg}`);
|
|
132
|
-
};
|
|
132
|
+
const onProgress = createProgressReporter();
|
|
133
133
|
|
|
134
134
|
// Get deposit key if available
|
|
135
135
|
const depositKey = process.env.DEPOSIT_KEY || (keypair ? keypair.depositKey() : null);
|
|
136
136
|
|
|
137
137
|
// Single pool mode -- flat output (backwards compatible)
|
|
138
138
|
if (poolsToQuery.length === 1) {
|
|
139
|
-
const poolResult = await
|
|
139
|
+
const [poolResult, walletBalances] = await Promise.all([
|
|
140
|
+
fetchPoolBalance(poolsToQuery[0], address, keypair, rpcUrl, onProgress),
|
|
141
|
+
getWalletBalances(address, rpcUrl),
|
|
142
|
+
]);
|
|
140
143
|
|
|
141
|
-
|
|
142
|
-
if (!options.quiet) process.stderr.write('\r\x1b[K');
|
|
144
|
+
clearProgress();
|
|
143
145
|
|
|
144
146
|
const output = {
|
|
145
147
|
address,
|
|
146
148
|
depositKey: depositKey || null,
|
|
149
|
+
wallet: walletBalances,
|
|
147
150
|
...poolResult,
|
|
148
151
|
};
|
|
149
152
|
|
|
150
|
-
|
|
153
|
+
if (options.json) {
|
|
154
|
+
printJson(output);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
printCombinedBalanceHuman(output);
|
|
151
159
|
return;
|
|
152
160
|
}
|
|
153
161
|
|
|
154
162
|
// All pools mode -- nested output
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
const poolPromises = poolsToQuery.map(pool =>
|
|
164
|
+
fetchPoolBalance(pool, address, keypair, rpcUrl, onProgress),
|
|
165
|
+
);
|
|
166
|
+
const [walletBalances, ...poolResults] = await Promise.all([
|
|
167
|
+
getWalletBalances(address, rpcUrl),
|
|
168
|
+
...poolPromises,
|
|
169
|
+
]);
|
|
160
170
|
|
|
161
|
-
|
|
162
|
-
if (!options.quiet) process.stderr.write('\r\x1b[K');
|
|
171
|
+
clearProgress();
|
|
163
172
|
|
|
164
173
|
const output = {
|
|
165
174
|
address,
|
|
166
175
|
depositKey: depositKey || null,
|
|
167
|
-
|
|
176
|
+
wallet: walletBalances,
|
|
177
|
+
pools: poolResults,
|
|
168
178
|
};
|
|
169
179
|
|
|
170
|
-
|
|
180
|
+
if (options.json) {
|
|
181
|
+
printJson(output);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
printMultiPoolBalanceHuman(output);
|
|
171
186
|
} catch (error) {
|
|
172
|
-
|
|
187
|
+
clearProgress();
|
|
173
188
|
handleCLIError(error);
|
|
174
189
|
}
|
|
175
190
|
});
|
|
176
191
|
|
|
192
|
+
balance.addCommand(createQueueBalanceCommand('queue'));
|
|
193
|
+
balance.addCommand(createPrivateBalanceCommand('private'));
|
|
194
|
+
|
|
177
195
|
return balance;
|
|
178
196
|
}
|
|
197
|
+
|
|
198
|
+
function printPoolHuman(output: Record<string, unknown>): void {
|
|
199
|
+
const symbol = output.symbol as string;
|
|
200
|
+
|
|
201
|
+
printSection(`${output.pool}`);
|
|
202
|
+
printFields([
|
|
203
|
+
{ label: 'Total', value: `${output.totalBalance} ${symbol}` },
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
const privateData = output.private as { balance?: string | null; balanceWei?: string; utxoCount?: number; note?: string; utxos?: Array<{ index: number; amount: string }> };
|
|
207
|
+
if (privateData.note) {
|
|
208
|
+
printFields([
|
|
209
|
+
{ label: 'Private', value: privateData.note },
|
|
210
|
+
]);
|
|
211
|
+
} else {
|
|
212
|
+
printFields([
|
|
213
|
+
{ label: 'Private', value: `${privateData.balance} ${symbol}` },
|
|
214
|
+
]);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const queueData = output.queue as { balance: string; balanceWei: string; count: number; deposits: Array<{ nonce: string | number; amount: string; status: string }> };
|
|
218
|
+
printFields([
|
|
219
|
+
{ label: 'Queue', value: `${queueData.balance} ${symbol}` },
|
|
220
|
+
{ label: 'Pending', value: queueData.count },
|
|
221
|
+
]);
|
|
222
|
+
if (queueData.deposits.length > 0) {
|
|
223
|
+
printList(
|
|
224
|
+
queueData.deposits.map((d) => `nonce ${d.nonce}: ${d.amount} (${d.status})`)
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function printCombinedBalanceHuman(output: Record<string, unknown>): void {
|
|
230
|
+
const wallet = output.wallet as { eth: string; usdc: string };
|
|
231
|
+
|
|
232
|
+
printHeader(`${output.pool} Balance`);
|
|
233
|
+
printFields([
|
|
234
|
+
{ label: 'Address', value: output.address },
|
|
235
|
+
{ label: 'Deposit key', value: typeof output.depositKey === 'string' ? maskValue(output.depositKey) : 'not set' },
|
|
236
|
+
]);
|
|
237
|
+
|
|
238
|
+
printSection('Wallet (public)');
|
|
239
|
+
printFields([
|
|
240
|
+
{ label: 'ETH', value: `${wallet.eth} ETH` },
|
|
241
|
+
{ label: 'USDC', value: `${wallet.usdc} USDC` },
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
printPoolHuman(output);
|
|
245
|
+
printLine();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function printMultiPoolBalanceHuman(output: { address: string; depositKey: string | null; wallet: { eth: string; usdc: string }; pools: Record<string, unknown>[] }): void {
|
|
249
|
+
printHeader('Balances');
|
|
250
|
+
printFields([
|
|
251
|
+
{ label: 'Address', value: output.address },
|
|
252
|
+
{ label: 'Deposit key', value: output.depositKey ? maskValue(output.depositKey) : 'not set' },
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
printSection('Wallet (public)');
|
|
256
|
+
printFields([
|
|
257
|
+
{ label: 'ETH', value: `${output.wallet.eth} ETH` },
|
|
258
|
+
{ label: 'USDC', value: `${output.wallet.usdc} USDC` },
|
|
259
|
+
]);
|
|
260
|
+
|
|
261
|
+
for (const poolResult of output.pools) {
|
|
262
|
+
printPoolHuman(poolResult);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
printLine();
|
|
266
|
+
}
|
|
@@ -6,88 +6,118 @@ import { Command } from 'commander';
|
|
|
6
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
20
|
};
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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 };
|
|
23
43
|
}
|
|
24
44
|
|
|
25
45
|
const SUPPORTED_ASSETS = ['ETH', 'USDC'];
|
|
26
46
|
|
|
27
|
-
// Progress helper - writes to stderr so JSON output stays clean
|
|
28
|
-
function progress(msg: string, quiet?: boolean) {
|
|
29
|
-
if (!quiet) {
|
|
30
|
-
process.stderr.write(`\r\x1b[K${msg}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
47
|
export function createDepositCommand(): Command {
|
|
35
48
|
const deposit = new Command('deposit')
|
|
36
49
|
.description('Deposit ETH or USDC into Veil')
|
|
37
50
|
.argument('<asset>', 'Asset to deposit (ETH or USDC)')
|
|
38
|
-
.argument('<amount>', 'Amount to deposit
|
|
39
|
-
.option('--
|
|
40
|
-
.option('--
|
|
41
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
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
|
+
`)
|
|
44
64
|
.action(async (asset: string, amount: string, options) => {
|
|
45
65
|
try {
|
|
46
66
|
const assetUpper = asset.toUpperCase();
|
|
47
67
|
|
|
48
|
-
// Validate asset
|
|
49
68
|
if (!SUPPORTED_ASSETS.includes(assetUpper)) {
|
|
50
69
|
throw new CLIError(ErrorCode.INVALID_AMOUNT, `Unsupported asset: ${asset}. Supported: ${SUPPORTED_ASSETS.join(', ')}`);
|
|
51
70
|
}
|
|
52
71
|
|
|
53
72
|
const amountNum = parseFloat(amount);
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Check minimum deposit
|
|
58
|
-
if (amountNum < minimumWithFee) {
|
|
73
|
+
const minimumNet = MINIMUM_NET[assetUpper];
|
|
74
|
+
|
|
75
|
+
if (amountNum < minimumNet) {
|
|
59
76
|
throw new CLIError(
|
|
60
77
|
ErrorCode.INVALID_AMOUNT,
|
|
61
|
-
`Minimum deposit is ${minimumNet} ${assetUpper}
|
|
62
|
-
`With ${DEPOSIT_FEE_PERCENT}% fee, send at least ${minimumWithFee.toFixed(assetUpper === 'ETH' ? 5 : 8)} ${assetUpper}.`
|
|
78
|
+
`Minimum deposit is ${minimumNet} ${assetUpper}.`
|
|
63
79
|
);
|
|
64
80
|
}
|
|
65
81
|
|
|
66
|
-
|
|
67
|
-
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;
|
|
68
100
|
if (!depositKey) {
|
|
69
|
-
throw new CLIError(ErrorCode.DEPOSIT_KEY_MISSING, '
|
|
101
|
+
throw new CLIError(ErrorCode.DEPOSIT_KEY_MISSING, 'DEPOSIT_KEY not set. Run "veil init" first.');
|
|
70
102
|
}
|
|
71
103
|
|
|
72
|
-
progress('Building transaction...'
|
|
104
|
+
progress('Building transaction...');
|
|
73
105
|
|
|
74
|
-
// Build the deposit transaction
|
|
75
106
|
let tx: TransactionData;
|
|
76
107
|
let approveTx: TransactionData | null = null;
|
|
77
108
|
|
|
78
109
|
if (assetUpper === 'USDC') {
|
|
79
|
-
approveTx = buildApproveUSDCTx({ amount });
|
|
80
|
-
tx = buildDepositUSDCTx({ depositKey, amount });
|
|
110
|
+
approveTx = buildApproveUSDCTx({ amount: grossStr });
|
|
111
|
+
tx = buildDepositUSDCTx({ depositKey, amount: grossStr });
|
|
81
112
|
} else {
|
|
82
|
-
tx = buildDepositETHTx({ depositKey, amount });
|
|
113
|
+
tx = buildDepositETHTx({ depositKey, amount: grossStr });
|
|
83
114
|
}
|
|
84
115
|
|
|
85
|
-
//
|
|
116
|
+
// --unsigned mode
|
|
86
117
|
if (options.unsigned) {
|
|
87
|
-
|
|
118
|
+
clearProgress();
|
|
88
119
|
const payloads: Record<string, unknown>[] = [];
|
|
89
120
|
|
|
90
|
-
// Include approval tx for ERC20 tokens
|
|
91
121
|
if (approveTx) {
|
|
92
122
|
payloads.push({
|
|
93
123
|
step: 'approve',
|
|
@@ -106,52 +136,95 @@ export function createDepositCommand(): Command {
|
|
|
106
136
|
chainId: 8453,
|
|
107
137
|
});
|
|
108
138
|
|
|
109
|
-
|
|
139
|
+
printJson(payloads.length === 1 ? payloads[0] : payloads);
|
|
110
140
|
return;
|
|
111
141
|
}
|
|
112
142
|
|
|
113
|
-
// Regular mode: sign and send
|
|
114
143
|
const config = getConfig(options);
|
|
115
144
|
const address = getAddress(config.privateKey);
|
|
116
145
|
|
|
117
|
-
// For ETH deposits, check ETH balance
|
|
118
146
|
if (assetUpper === 'ETH') {
|
|
119
|
-
progress('Checking balance...'
|
|
147
|
+
progress('Checking balance...');
|
|
120
148
|
const balance = await getBalance(address, config.rpcUrl);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
+
);
|
|
126
156
|
}
|
|
127
157
|
}
|
|
128
158
|
|
|
129
|
-
// Send approval transaction for ERC20 tokens
|
|
130
159
|
if (approveTx) {
|
|
131
|
-
progress(`Approving ${assetUpper}
|
|
132
|
-
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
|
+
}
|
|
133
193
|
}
|
|
134
194
|
|
|
135
|
-
progress('Sending deposit transaction...'
|
|
136
|
-
|
|
137
|
-
// Send the deposit transaction
|
|
195
|
+
progress('Sending deposit transaction...');
|
|
138
196
|
const result = await sendTransaction(config, tx);
|
|
197
|
+
progress('Confirming...');
|
|
198
|
+
clearProgress();
|
|
139
199
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// Clear progress line
|
|
143
|
-
progress('', options.quiet);
|
|
144
|
-
|
|
145
|
-
console.log(JSON.stringify({
|
|
200
|
+
const output = {
|
|
146
201
|
success: result.receipt.status === 'success',
|
|
147
202
|
hash: result.hash,
|
|
148
203
|
asset: assetUpper,
|
|
149
204
|
amount,
|
|
205
|
+
fee: feeStr,
|
|
206
|
+
totalSent: grossStr,
|
|
150
207
|
blockNumber: result.receipt.blockNumber.toString(),
|
|
151
|
-
|
|
152
|
-
|
|
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();
|
|
153
226
|
} catch (error) {
|
|
154
|
-
|
|
227
|
+
clearProgress();
|
|
155
228
|
handleCLIError(error);
|
|
156
229
|
}
|
|
157
230
|
});
|