@veil-cash/sdk 0.6.1 → 0.6.3
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 +12 -7
- package/SDK.md +38 -7
- package/dist/cli/index.cjs +523 -220
- package/dist/index.cjs +213 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +121 -2
- package/dist/index.d.ts +121 -2
- package/dist/index.js +211 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/veil/SKILL.md +32 -12
- package/skills/veil/reference.md +28 -8
- package/src/abi.ts +8 -0
- package/src/balance.ts +48 -0
- package/src/cli/commands/deposit.ts +61 -15
- package/src/cli/commands/subaccount.ts +82 -2
- package/src/cli/index.ts +1 -1
- package/src/index.ts +7 -0
- package/src/subaccount.ts +244 -5
- package/src/types.ts +56 -0
package/dist/index.cjs
CHANGED
|
@@ -625,6 +625,14 @@ var QUEUE_ABI = [
|
|
|
625
625
|
outputs: [{ name: "count", type: "uint256" }],
|
|
626
626
|
stateMutability: "view",
|
|
627
627
|
type: "function"
|
|
628
|
+
},
|
|
629
|
+
// Get remaining daily free deposits for an address (V3+)
|
|
630
|
+
{
|
|
631
|
+
inputs: [{ name: "_depositor", type: "address" }],
|
|
632
|
+
name: "getDailyFreeRemaining",
|
|
633
|
+
outputs: [{ name: "remaining", type: "uint256" }],
|
|
634
|
+
stateMutability: "view",
|
|
635
|
+
type: "function"
|
|
628
636
|
}
|
|
629
637
|
];
|
|
630
638
|
var POOL_ABI = [
|
|
@@ -1342,6 +1350,25 @@ async function getQueueBalance(options) {
|
|
|
1342
1350
|
pendingCount: pendingDeposits.length
|
|
1343
1351
|
};
|
|
1344
1352
|
}
|
|
1353
|
+
async function getDailyFreeRemaining(options) {
|
|
1354
|
+
const { address, pool = "eth", rpcUrl } = options;
|
|
1355
|
+
const queueAddress = getQueueAddress(pool);
|
|
1356
|
+
const publicClient = viem.createPublicClient({
|
|
1357
|
+
chain: chains.base,
|
|
1358
|
+
transport: viem.http(rpcUrl)
|
|
1359
|
+
});
|
|
1360
|
+
try {
|
|
1361
|
+
const remaining = await publicClient.readContract({
|
|
1362
|
+
address: queueAddress,
|
|
1363
|
+
abi: QUEUE_ABI,
|
|
1364
|
+
functionName: "getDailyFreeRemaining",
|
|
1365
|
+
args: [address]
|
|
1366
|
+
});
|
|
1367
|
+
return Number(remaining);
|
|
1368
|
+
} catch {
|
|
1369
|
+
return 0;
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1345
1372
|
async function getPrivateBalance(options) {
|
|
1346
1373
|
const { keypair, pool = "eth", rpcUrl, onProgress } = options;
|
|
1347
1374
|
const poolAddress = getPoolAddress(pool);
|
|
@@ -1669,7 +1696,16 @@ async function postRelayJson(endpoint, body, relayUrl) {
|
|
|
1669
1696
|
},
|
|
1670
1697
|
body: JSON.stringify(body)
|
|
1671
1698
|
});
|
|
1672
|
-
const
|
|
1699
|
+
const text = await response.text();
|
|
1700
|
+
let data;
|
|
1701
|
+
try {
|
|
1702
|
+
data = JSON.parse(text);
|
|
1703
|
+
} catch {
|
|
1704
|
+
throw new RelayError(
|
|
1705
|
+
`Relay returned non-JSON response (HTTP ${response.status})`,
|
|
1706
|
+
response.status
|
|
1707
|
+
);
|
|
1708
|
+
}
|
|
1673
1709
|
if (!response.ok) {
|
|
1674
1710
|
const errorData = data;
|
|
1675
1711
|
throw new RelayError(
|
|
@@ -2241,11 +2277,12 @@ function deriveSubaccountChildDepositKey(childPrivateKey) {
|
|
|
2241
2277
|
}
|
|
2242
2278
|
async function predictSubaccountForwarder(options) {
|
|
2243
2279
|
const publicClient = createBaseClient(options.rpcUrl);
|
|
2280
|
+
const depositKeyBytes = options.childDepositKey.startsWith("0x") ? options.childDepositKey : `0x${options.childDepositKey}`;
|
|
2244
2281
|
return publicClient.readContract({
|
|
2245
2282
|
abi: FORWARDER_FACTORY_ABI,
|
|
2246
2283
|
address: getForwarderFactoryAddress(),
|
|
2247
2284
|
functionName: "computeAddress",
|
|
2248
|
-
args: [options.salt,
|
|
2285
|
+
args: [options.salt, depositKeyBytes, options.childOwner]
|
|
2249
2286
|
});
|
|
2250
2287
|
}
|
|
2251
2288
|
async function deriveSubaccountSlot(options) {
|
|
@@ -2282,7 +2319,7 @@ async function deploySubaccountForwarder(options) {
|
|
|
2282
2319
|
slot: options.slot,
|
|
2283
2320
|
rpcUrl: options.rpcUrl
|
|
2284
2321
|
});
|
|
2285
|
-
|
|
2322
|
+
const result = await postRelayJson(
|
|
2286
2323
|
"/stealth/deploy",
|
|
2287
2324
|
{
|
|
2288
2325
|
salt: slot.salt,
|
|
@@ -2292,6 +2329,7 @@ async function deploySubaccountForwarder(options) {
|
|
|
2292
2329
|
},
|
|
2293
2330
|
options.relayUrl
|
|
2294
2331
|
);
|
|
2332
|
+
return { ...result, slot };
|
|
2295
2333
|
}
|
|
2296
2334
|
async function sweepSubaccountForwarder(options) {
|
|
2297
2335
|
const asset = normalizeAsset(options.asset);
|
|
@@ -2316,11 +2354,32 @@ function toQueueStatus(asset, result) {
|
|
|
2316
2354
|
pendingDeposits: result.pendingDeposits
|
|
2317
2355
|
};
|
|
2318
2356
|
}
|
|
2357
|
+
function toPrivateBalanceStatus(result) {
|
|
2358
|
+
return {
|
|
2359
|
+
privateBalance: result.privateBalance,
|
|
2360
|
+
privateBalanceWei: result.privateBalanceWei,
|
|
2361
|
+
utxoCount: result.utxoCount,
|
|
2362
|
+
spentCount: result.spentCount,
|
|
2363
|
+
unspentCount: result.unspentCount
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
async function getSubaccountPrivateBalance(options) {
|
|
2367
|
+
const normalizedSlot = normalizeSlot(options.slot);
|
|
2368
|
+
assertPrivateKey(options.rootPrivateKey, "rootPrivateKey");
|
|
2369
|
+
const childPrivateKey = deriveSubaccountChildPrivateKey(options.rootPrivateKey, normalizedSlot);
|
|
2370
|
+
const childKeypair = new Keypair(childPrivateKey);
|
|
2371
|
+
return getPrivateBalance({
|
|
2372
|
+
keypair: childKeypair,
|
|
2373
|
+
pool: options.pool,
|
|
2374
|
+
rpcUrl: options.rpcUrl,
|
|
2375
|
+
onProgress: options.onProgress
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
2319
2378
|
async function getSubaccountStatus(options) {
|
|
2320
2379
|
const slot = await deriveSubaccountSlot(options);
|
|
2321
2380
|
const publicClient = createBaseClient(options.rpcUrl);
|
|
2322
2381
|
const addresses = getAddresses();
|
|
2323
|
-
const [deployed, ethWei, usdcWei, ethQueue, usdcQueue] = await Promise.all([
|
|
2382
|
+
const [deployed, ethWei, usdcWei, ethQueue, usdcQueue, ethPrivate, usdcPrivate] = await Promise.all([
|
|
2324
2383
|
isSubaccountForwarderDeployed({
|
|
2325
2384
|
forwarderAddress: slot.forwarderAddress,
|
|
2326
2385
|
rpcUrl: options.rpcUrl
|
|
@@ -2341,6 +2400,18 @@ async function getSubaccountStatus(options) {
|
|
|
2341
2400
|
address: slot.forwarderAddress,
|
|
2342
2401
|
pool: "usdc",
|
|
2343
2402
|
rpcUrl: options.rpcUrl
|
|
2403
|
+
}),
|
|
2404
|
+
getSubaccountPrivateBalance({
|
|
2405
|
+
rootPrivateKey: options.rootPrivateKey,
|
|
2406
|
+
slot: options.slot,
|
|
2407
|
+
pool: "eth",
|
|
2408
|
+
rpcUrl: options.rpcUrl
|
|
2409
|
+
}),
|
|
2410
|
+
getSubaccountPrivateBalance({
|
|
2411
|
+
rootPrivateKey: options.rootPrivateKey,
|
|
2412
|
+
slot: options.slot,
|
|
2413
|
+
pool: "usdc",
|
|
2414
|
+
rpcUrl: options.rpcUrl
|
|
2344
2415
|
})
|
|
2345
2416
|
]);
|
|
2346
2417
|
return {
|
|
@@ -2356,6 +2427,10 @@ async function getSubaccountStatus(options) {
|
|
|
2356
2427
|
balanceWei: usdcWei.toString()
|
|
2357
2428
|
}
|
|
2358
2429
|
},
|
|
2430
|
+
privateBalances: {
|
|
2431
|
+
eth: toPrivateBalanceStatus(ethPrivate),
|
|
2432
|
+
usdc: toPrivateBalanceStatus(usdcPrivate)
|
|
2433
|
+
},
|
|
2359
2434
|
queues: {
|
|
2360
2435
|
eth: toQueueStatus("eth", ethQueue),
|
|
2361
2436
|
usdc: toQueueStatus("usdc", usdcQueue)
|
|
@@ -2502,6 +2577,137 @@ async function buildSubaccountRecoveryTx(options) {
|
|
|
2502
2577
|
signature
|
|
2503
2578
|
};
|
|
2504
2579
|
}
|
|
2580
|
+
async function mergeSubaccount(options) {
|
|
2581
|
+
const {
|
|
2582
|
+
rootPrivateKey,
|
|
2583
|
+
slot,
|
|
2584
|
+
pool = "eth",
|
|
2585
|
+
rpcUrl,
|
|
2586
|
+
relayUrl,
|
|
2587
|
+
onProgress
|
|
2588
|
+
} = options;
|
|
2589
|
+
const normalizedSlot = normalizeSlot(slot);
|
|
2590
|
+
assertPrivateKey(rootPrivateKey, "rootPrivateKey");
|
|
2591
|
+
const poolConfig = POOL_CONFIG[pool];
|
|
2592
|
+
const poolAddress = getPoolAddress(pool);
|
|
2593
|
+
const childPrivateKey = deriveSubaccountChildPrivateKey(rootPrivateKey, normalizedSlot);
|
|
2594
|
+
const childKeypair = new Keypair(childPrivateKey);
|
|
2595
|
+
const parentKeypair = new Keypair(rootPrivateKey);
|
|
2596
|
+
onProgress?.("Fetching subaccount balance...");
|
|
2597
|
+
const balanceResult = await getPrivateBalance({
|
|
2598
|
+
keypair: childKeypair,
|
|
2599
|
+
pool,
|
|
2600
|
+
rpcUrl,
|
|
2601
|
+
onProgress
|
|
2602
|
+
});
|
|
2603
|
+
const unspentUtxoInfos = balanceResult.utxos.filter((u) => !u.isSpent);
|
|
2604
|
+
if (unspentUtxoInfos.length === 0) {
|
|
2605
|
+
throw new Error("Subaccount has no unspent UTXOs to merge");
|
|
2606
|
+
}
|
|
2607
|
+
if (unspentUtxoInfos.length > 16) {
|
|
2608
|
+
throw new Error(
|
|
2609
|
+
`Subaccount has ${unspentUtxoInfos.length} unspent UTXOs which exceeds the 16-input circuit limit. Consolidate UTXOs on the subaccount first before merging.`
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
onProgress?.("Preparing UTXOs...");
|
|
2613
|
+
const publicClient = viem.createPublicClient({
|
|
2614
|
+
chain: chains.base,
|
|
2615
|
+
transport: viem.http(rpcUrl)
|
|
2616
|
+
});
|
|
2617
|
+
const utxos = [];
|
|
2618
|
+
for (const utxoInfo of unspentUtxoInfos) {
|
|
2619
|
+
const encryptedOutputs = await publicClient.readContract({
|
|
2620
|
+
address: poolAddress,
|
|
2621
|
+
abi: POOL_ABI,
|
|
2622
|
+
functionName: "getEncryptedOutputs",
|
|
2623
|
+
args: [BigInt(utxoInfo.index), BigInt(utxoInfo.index + 1)]
|
|
2624
|
+
});
|
|
2625
|
+
if (encryptedOutputs.length > 0) {
|
|
2626
|
+
try {
|
|
2627
|
+
const utxo = Utxo.decrypt(encryptedOutputs[0], childKeypair);
|
|
2628
|
+
utxo.index = utxoInfo.index;
|
|
2629
|
+
utxos.push(utxo);
|
|
2630
|
+
} catch {
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
if (utxos.length === 0) {
|
|
2635
|
+
throw new Error("Failed to decrypt subaccount UTXOs");
|
|
2636
|
+
}
|
|
2637
|
+
onProgress?.("Selecting UTXOs...");
|
|
2638
|
+
const amount = balanceResult.privateBalance;
|
|
2639
|
+
const { selectedUtxos, changeAmount } = selectUtxosForWithdraw(
|
|
2640
|
+
utxos,
|
|
2641
|
+
amount,
|
|
2642
|
+
poolConfig.decimals
|
|
2643
|
+
);
|
|
2644
|
+
const outputs = [];
|
|
2645
|
+
const mergeWei = viem.parseUnits(amount, poolConfig.decimals);
|
|
2646
|
+
outputs.push(new Utxo({ amount: mergeWei, keypair: parentKeypair }));
|
|
2647
|
+
if (changeAmount > 0n) {
|
|
2648
|
+
outputs.push(new Utxo({ amount: changeAmount, keypair: parentKeypair }));
|
|
2649
|
+
}
|
|
2650
|
+
onProgress?.("Fetching commitments...");
|
|
2651
|
+
const nextIndex = await publicClient.readContract({
|
|
2652
|
+
address: poolAddress,
|
|
2653
|
+
abi: POOL_ABI,
|
|
2654
|
+
functionName: "nextIndex"
|
|
2655
|
+
});
|
|
2656
|
+
const BATCH_SIZE = 5e3;
|
|
2657
|
+
const commitments = [];
|
|
2658
|
+
const totalBatches = Math.ceil(nextIndex / BATCH_SIZE);
|
|
2659
|
+
for (let start = 0; start < nextIndex; start += BATCH_SIZE) {
|
|
2660
|
+
const end = Math.min(start + BATCH_SIZE, nextIndex);
|
|
2661
|
+
const batchNum = Math.floor(start / BATCH_SIZE) + 1;
|
|
2662
|
+
onProgress?.("Fetching commitments", `batch ${batchNum}/${totalBatches}`);
|
|
2663
|
+
const batch = await publicClient.readContract({
|
|
2664
|
+
address: poolAddress,
|
|
2665
|
+
abi: POOL_ABI,
|
|
2666
|
+
functionName: "getCommitments",
|
|
2667
|
+
args: [BigInt(start), BigInt(end)]
|
|
2668
|
+
});
|
|
2669
|
+
commitments.push(...batch.map((c) => c.toString()));
|
|
2670
|
+
}
|
|
2671
|
+
onProgress?.("Building ZK proof...");
|
|
2672
|
+
const result = await prepareTransaction({
|
|
2673
|
+
commitments,
|
|
2674
|
+
inputs: selectedUtxos,
|
|
2675
|
+
outputs,
|
|
2676
|
+
fee: 0,
|
|
2677
|
+
recipient: "0x0000000000000000000000000000000000000000",
|
|
2678
|
+
relayer: "0x0000000000000000000000000000000000000000",
|
|
2679
|
+
onProgress
|
|
2680
|
+
});
|
|
2681
|
+
onProgress?.("Submitting to relay...");
|
|
2682
|
+
const relayResult = await submitRelay({
|
|
2683
|
+
type: "transfer",
|
|
2684
|
+
pool,
|
|
2685
|
+
relayUrl,
|
|
2686
|
+
proofArgs: {
|
|
2687
|
+
proof: result.args.proof,
|
|
2688
|
+
root: result.args.root,
|
|
2689
|
+
inputNullifiers: result.args.inputNullifiers,
|
|
2690
|
+
outputCommitments: result.args.outputCommitments,
|
|
2691
|
+
publicAmount: result.args.publicAmount,
|
|
2692
|
+
extDataHash: result.args.extDataHash
|
|
2693
|
+
},
|
|
2694
|
+
extData: result.extData,
|
|
2695
|
+
metadata: {
|
|
2696
|
+
amount,
|
|
2697
|
+
recipient: "self",
|
|
2698
|
+
inputUtxoCount: selectedUtxos.length,
|
|
2699
|
+
outputUtxoCount: outputs.length
|
|
2700
|
+
}
|
|
2701
|
+
});
|
|
2702
|
+
return {
|
|
2703
|
+
success: relayResult.success,
|
|
2704
|
+
transactionHash: relayResult.transactionHash,
|
|
2705
|
+
blockNumber: relayResult.blockNumber,
|
|
2706
|
+
amount,
|
|
2707
|
+
slot: normalizedSlot,
|
|
2708
|
+
pool
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2505
2711
|
|
|
2506
2712
|
exports.ADDRESSES = ADDRESSES;
|
|
2507
2713
|
exports.CIRCUIT_CONFIG = CIRCUIT_CONFIG;
|
|
@@ -2541,6 +2747,7 @@ exports.deriveSubaccountSalt = deriveSubaccountSalt;
|
|
|
2541
2747
|
exports.deriveSubaccountSlot = deriveSubaccountSlot;
|
|
2542
2748
|
exports.findNextSubaccountWithdrawNonce = findNextSubaccountWithdrawNonce;
|
|
2543
2749
|
exports.getAddresses = getAddresses;
|
|
2750
|
+
exports.getDailyFreeRemaining = getDailyFreeRemaining;
|
|
2544
2751
|
exports.getExtDataHash = getExtDataHash;
|
|
2545
2752
|
exports.getForwarderFactoryAddress = getForwarderFactoryAddress;
|
|
2546
2753
|
exports.getMerklePath = getMerklePath;
|
|
@@ -2550,9 +2757,11 @@ exports.getQueueAddress = getQueueAddress;
|
|
|
2550
2757
|
exports.getQueueBalance = getQueueBalance;
|
|
2551
2758
|
exports.getRelayInfo = getRelayInfo;
|
|
2552
2759
|
exports.getRelayUrl = getRelayUrl;
|
|
2760
|
+
exports.getSubaccountPrivateBalance = getSubaccountPrivateBalance;
|
|
2553
2761
|
exports.getSubaccountStatus = getSubaccountStatus;
|
|
2554
2762
|
exports.isSubaccountForwarderDeployed = isSubaccountForwarderDeployed;
|
|
2555
2763
|
exports.isSubaccountWithdrawNonceUsed = isSubaccountWithdrawNonceUsed;
|
|
2764
|
+
exports.mergeSubaccount = mergeSubaccount;
|
|
2556
2765
|
exports.mergeUtxos = mergeUtxos;
|
|
2557
2766
|
exports.packEncryptedMessage = packEncryptedMessage;
|
|
2558
2767
|
exports.poseidonHash = poseidonHash;
|