@veil-cash/sdk 0.6.0 → 0.6.2

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/dist/index.d.cts CHANGED
@@ -406,6 +406,23 @@ interface SubaccountBalances {
406
406
  eth: SubaccountAssetBalance;
407
407
  usdc: SubaccountAssetBalance;
408
408
  }
409
+ /**
410
+ * Private pool balance summary for a specific asset
411
+ */
412
+ interface SubaccountPrivateBalanceStatus {
413
+ privateBalance: string;
414
+ privateBalanceWei: string;
415
+ utxoCount: number;
416
+ spentCount: number;
417
+ unspentCount: number;
418
+ }
419
+ /**
420
+ * Private pool balances for both supported assets
421
+ */
422
+ interface SubaccountPrivateBalances {
423
+ eth: SubaccountPrivateBalanceStatus;
424
+ usdc: SubaccountPrivateBalanceStatus;
425
+ }
409
426
  /**
410
427
  * Queue status for a specific asset
411
428
  */
@@ -423,6 +440,7 @@ interface SubaccountStatusResult {
423
440
  slot: SubaccountSlot;
424
441
  deployed: boolean;
425
442
  balances: SubaccountBalances;
443
+ privateBalances: SubaccountPrivateBalances;
426
444
  queues: {
427
445
  eth: SubaccountQueueStatus;
428
446
  usdc: SubaccountQueueStatus;
@@ -453,6 +471,40 @@ interface SubaccountWithdrawTypedData {
453
471
  deadline: bigint;
454
472
  };
455
473
  }
474
+ /**
475
+ * Options for merging a subaccount's private balance back to the main wallet
476
+ */
477
+ interface SubaccountMergeOptions {
478
+ /** Root private key (VEIL_KEY) */
479
+ rootPrivateKey: `0x${string}`;
480
+ /** Subaccount slot (0-2) */
481
+ slot: number;
482
+ /** Pool to merge in (default: 'eth') */
483
+ pool?: RelayPool;
484
+ /** Optional RPC URL */
485
+ rpcUrl?: string;
486
+ /** Optional relay URL */
487
+ relayUrl?: string;
488
+ /** Progress callback */
489
+ onProgress?: (stage: string, detail?: string) => void;
490
+ }
491
+ /**
492
+ * Result from merging a subaccount's balance to the main wallet
493
+ */
494
+ interface SubaccountMergeResult {
495
+ /** Whether the merge was successful */
496
+ success: boolean;
497
+ /** Transaction hash */
498
+ transactionHash: string;
499
+ /** Block number of the transaction */
500
+ blockNumber: string;
501
+ /** Amount merged (human-readable) */
502
+ amount: string;
503
+ /** Subaccount slot that was merged */
504
+ slot: number;
505
+ /** Pool the merge was executed in */
506
+ pool: RelayPool;
507
+ }
456
508
  /**
457
509
  * Built recovery transaction and signing metadata
458
510
  */
@@ -2587,8 +2639,17 @@ declare function isSubaccountForwarderDeployed(options: {
2587
2639
  forwarderAddress: `0x${string}`;
2588
2640
  rpcUrl?: string;
2589
2641
  }): Promise<boolean>;
2590
- declare function deploySubaccountForwarder(options: SubaccountDeployRequest): Promise<SubaccountRelayResult>;
2642
+ declare function deploySubaccountForwarder(options: SubaccountDeployRequest): Promise<SubaccountRelayResult & {
2643
+ slot: SubaccountSlot;
2644
+ }>;
2591
2645
  declare function sweepSubaccountForwarder(options: SubaccountSweepRequest): Promise<SubaccountRelayResult>;
2646
+ declare function getSubaccountPrivateBalance(options: {
2647
+ rootPrivateKey: `0x${string}`;
2648
+ slot: number;
2649
+ pool?: 'eth' | 'usdc';
2650
+ rpcUrl?: string;
2651
+ onProgress?: (stage: string, detail?: string) => void;
2652
+ }): Promise<PrivateBalanceResult>;
2592
2653
  declare function getSubaccountStatus(options: {
2593
2654
  rootPrivateKey: `0x${string}`;
2594
2655
  slot: number;
@@ -2627,6 +2688,27 @@ declare function buildSubaccountRecoveryTx(options: {
2627
2688
  deadline?: bigint | number;
2628
2689
  rpcUrl?: string;
2629
2690
  }): Promise<SubaccountRecoveryResult>;
2691
+ /**
2692
+ * Merge a subaccount's entire private balance back to the main wallet.
2693
+ *
2694
+ * Builds a ZK transfer proof that moves every unspent UTXO belonging to the
2695
+ * child keypair into a new UTXO encrypted to the parent (root) keypair,
2696
+ * then submits it via the relay.
2697
+ *
2698
+ * @param options - Merge options
2699
+ * @returns Merge result with transaction hash and amount
2700
+ *
2701
+ * @example
2702
+ * ```typescript
2703
+ * const result = await mergeSubaccount({
2704
+ * rootPrivateKey: process.env.VEIL_KEY as `0x${string}`,
2705
+ * slot: 0,
2706
+ * pool: 'eth',
2707
+ * });
2708
+ * console.log(`Merged ${result.amount} — tx: ${result.transactionHash}`);
2709
+ * ```
2710
+ */
2711
+ declare function mergeSubaccount(options: SubaccountMergeOptions): Promise<SubaccountMergeResult>;
2630
2712
 
2631
2713
  /**
2632
2714
  * Crypto utilities for Veil SDK
@@ -2697,4 +2779,4 @@ declare function getExtDataHash(extData: ExtDataInput): bigint;
2697
2779
  */
2698
2780
  declare function shuffle<T>(array: T[]): T[];
2699
2781
 
2700
- export { ADDRESSES, type BuildTransferProofOptions, type BuildWithdrawProofOptions, CIRCUIT_CONFIG, type DepositTxOptions, ENTRY_ABI, ERC20_ABI, type EncryptedMessage, type ExtData, type ExtDataInput, FIELD_SIZE, FORWARDER_ABI, FORWARDER_CONTRACT_VERSION, FORWARDER_FACTORY_ABI, Keypair, MAX_SUBACCOUNT_SLOTS, MERKLE_TREE_HEIGHT, type MessageSigner, type NetworkAddresses, POOL_ABI, POOL_CONFIG, type PendingDeposit, type PoolConfig, type PrepareTransactionParams, type PrivateBalanceResult, type ProgressCallback, type ProofArgs, type ProofBuildResult, type ProofInput, QUEUE_ABI, type QueueBalanceResult, type RegisterTxOptions, RelayError, type RelayErrorResponse, type RelayExtData, type RelayMetadata, type RelayPool, type RelayProofArgs, type RelayRequest, type RelayResponse, type RelayType, type SubaccountAsset, type SubaccountAssetBalance, type SubaccountBalances, type SubaccountDeployRequest, type SubaccountQueueStatus, type SubaccountRecoveryResult, type SubaccountRelayResult, type SubaccountSlot, type SubaccountStatusResult, type SubaccountSweepRequest, type SubaccountWithdrawTypedData, type SubmitRelayOptions, type Token, type TransactionData, type TransactionResult, type TransferResult, Utxo, type UtxoInfo, type UtxoParams, type UtxoSelectionResult, VEIL_SIGNED_MESSAGE, type WithdrawResult, buildApproveUSDCTx, buildChangeDepositKeyTx, buildDepositETHTx, buildDepositTx, buildDepositUSDCTx, buildMerkleTree, buildRegisterTx, buildSubaccountRecoveryTx, buildSubaccountWithdrawTypedData, buildTransferProof, buildWithdrawProof, checkRecipientRegistration, checkRelayHealth, deploySubaccountForwarder, deriveSubaccountChildDepositKey, deriveSubaccountChildOwner, deriveSubaccountChildPrivateKey, deriveSubaccountSalt, deriveSubaccountSlot, findNextSubaccountWithdrawNonce, getAddresses, getExtDataHash, getForwarderFactoryAddress, getMerklePath, getPoolAddress, getPrivateBalance, getQueueAddress, getQueueBalance, getRelayInfo, getRelayUrl, getSubaccountStatus, isSubaccountForwarderDeployed, isSubaccountWithdrawNonceUsed, mergeUtxos, packEncryptedMessage, poseidonHash, poseidonHash2, predictSubaccountForwarder, prepareTransaction, prove, randomBN, selectCircuit, selectUtxosForWithdraw, shuffle, signSubaccountWithdraw, submitRelay, sweepSubaccountForwarder, toBuffer, toFixedHex, transfer, unpackEncryptedMessage, withdraw };
2782
+ export { ADDRESSES, type BuildTransferProofOptions, type BuildWithdrawProofOptions, CIRCUIT_CONFIG, type DepositTxOptions, ENTRY_ABI, ERC20_ABI, type EncryptedMessage, type ExtData, type ExtDataInput, FIELD_SIZE, FORWARDER_ABI, FORWARDER_CONTRACT_VERSION, FORWARDER_FACTORY_ABI, Keypair, MAX_SUBACCOUNT_SLOTS, MERKLE_TREE_HEIGHT, type MessageSigner, type NetworkAddresses, POOL_ABI, POOL_CONFIG, type PendingDeposit, type PoolConfig, type PrepareTransactionParams, type PrivateBalanceResult, type ProgressCallback, type ProofArgs, type ProofBuildResult, type ProofInput, QUEUE_ABI, type QueueBalanceResult, type RegisterTxOptions, RelayError, type RelayErrorResponse, type RelayExtData, type RelayMetadata, type RelayPool, type RelayProofArgs, type RelayRequest, type RelayResponse, type RelayType, type SubaccountAsset, type SubaccountAssetBalance, type SubaccountBalances, type SubaccountDeployRequest, type SubaccountMergeOptions, type SubaccountMergeResult, type SubaccountPrivateBalanceStatus, type SubaccountPrivateBalances, type SubaccountQueueStatus, type SubaccountRecoveryResult, type SubaccountRelayResult, type SubaccountSlot, type SubaccountStatusResult, type SubaccountSweepRequest, type SubaccountWithdrawTypedData, type SubmitRelayOptions, type Token, type TransactionData, type TransactionResult, type TransferResult, Utxo, type UtxoInfo, type UtxoParams, type UtxoSelectionResult, VEIL_SIGNED_MESSAGE, type WithdrawResult, buildApproveUSDCTx, buildChangeDepositKeyTx, buildDepositETHTx, buildDepositTx, buildDepositUSDCTx, buildMerkleTree, buildRegisterTx, buildSubaccountRecoveryTx, buildSubaccountWithdrawTypedData, buildTransferProof, buildWithdrawProof, checkRecipientRegistration, checkRelayHealth, deploySubaccountForwarder, deriveSubaccountChildDepositKey, deriveSubaccountChildOwner, deriveSubaccountChildPrivateKey, deriveSubaccountSalt, deriveSubaccountSlot, findNextSubaccountWithdrawNonce, getAddresses, getExtDataHash, getForwarderFactoryAddress, getMerklePath, getPoolAddress, getPrivateBalance, getQueueAddress, getQueueBalance, getRelayInfo, getRelayUrl, getSubaccountPrivateBalance, getSubaccountStatus, isSubaccountForwarderDeployed, isSubaccountWithdrawNonceUsed, mergeSubaccount, mergeUtxos, packEncryptedMessage, poseidonHash, poseidonHash2, predictSubaccountForwarder, prepareTransaction, prove, randomBN, selectCircuit, selectUtxosForWithdraw, shuffle, signSubaccountWithdraw, submitRelay, sweepSubaccountForwarder, toBuffer, toFixedHex, transfer, unpackEncryptedMessage, withdraw };
package/dist/index.d.ts CHANGED
@@ -406,6 +406,23 @@ interface SubaccountBalances {
406
406
  eth: SubaccountAssetBalance;
407
407
  usdc: SubaccountAssetBalance;
408
408
  }
409
+ /**
410
+ * Private pool balance summary for a specific asset
411
+ */
412
+ interface SubaccountPrivateBalanceStatus {
413
+ privateBalance: string;
414
+ privateBalanceWei: string;
415
+ utxoCount: number;
416
+ spentCount: number;
417
+ unspentCount: number;
418
+ }
419
+ /**
420
+ * Private pool balances for both supported assets
421
+ */
422
+ interface SubaccountPrivateBalances {
423
+ eth: SubaccountPrivateBalanceStatus;
424
+ usdc: SubaccountPrivateBalanceStatus;
425
+ }
409
426
  /**
410
427
  * Queue status for a specific asset
411
428
  */
@@ -423,6 +440,7 @@ interface SubaccountStatusResult {
423
440
  slot: SubaccountSlot;
424
441
  deployed: boolean;
425
442
  balances: SubaccountBalances;
443
+ privateBalances: SubaccountPrivateBalances;
426
444
  queues: {
427
445
  eth: SubaccountQueueStatus;
428
446
  usdc: SubaccountQueueStatus;
@@ -453,6 +471,40 @@ interface SubaccountWithdrawTypedData {
453
471
  deadline: bigint;
454
472
  };
455
473
  }
474
+ /**
475
+ * Options for merging a subaccount's private balance back to the main wallet
476
+ */
477
+ interface SubaccountMergeOptions {
478
+ /** Root private key (VEIL_KEY) */
479
+ rootPrivateKey: `0x${string}`;
480
+ /** Subaccount slot (0-2) */
481
+ slot: number;
482
+ /** Pool to merge in (default: 'eth') */
483
+ pool?: RelayPool;
484
+ /** Optional RPC URL */
485
+ rpcUrl?: string;
486
+ /** Optional relay URL */
487
+ relayUrl?: string;
488
+ /** Progress callback */
489
+ onProgress?: (stage: string, detail?: string) => void;
490
+ }
491
+ /**
492
+ * Result from merging a subaccount's balance to the main wallet
493
+ */
494
+ interface SubaccountMergeResult {
495
+ /** Whether the merge was successful */
496
+ success: boolean;
497
+ /** Transaction hash */
498
+ transactionHash: string;
499
+ /** Block number of the transaction */
500
+ blockNumber: string;
501
+ /** Amount merged (human-readable) */
502
+ amount: string;
503
+ /** Subaccount slot that was merged */
504
+ slot: number;
505
+ /** Pool the merge was executed in */
506
+ pool: RelayPool;
507
+ }
456
508
  /**
457
509
  * Built recovery transaction and signing metadata
458
510
  */
@@ -2587,8 +2639,17 @@ declare function isSubaccountForwarderDeployed(options: {
2587
2639
  forwarderAddress: `0x${string}`;
2588
2640
  rpcUrl?: string;
2589
2641
  }): Promise<boolean>;
2590
- declare function deploySubaccountForwarder(options: SubaccountDeployRequest): Promise<SubaccountRelayResult>;
2642
+ declare function deploySubaccountForwarder(options: SubaccountDeployRequest): Promise<SubaccountRelayResult & {
2643
+ slot: SubaccountSlot;
2644
+ }>;
2591
2645
  declare function sweepSubaccountForwarder(options: SubaccountSweepRequest): Promise<SubaccountRelayResult>;
2646
+ declare function getSubaccountPrivateBalance(options: {
2647
+ rootPrivateKey: `0x${string}`;
2648
+ slot: number;
2649
+ pool?: 'eth' | 'usdc';
2650
+ rpcUrl?: string;
2651
+ onProgress?: (stage: string, detail?: string) => void;
2652
+ }): Promise<PrivateBalanceResult>;
2592
2653
  declare function getSubaccountStatus(options: {
2593
2654
  rootPrivateKey: `0x${string}`;
2594
2655
  slot: number;
@@ -2627,6 +2688,27 @@ declare function buildSubaccountRecoveryTx(options: {
2627
2688
  deadline?: bigint | number;
2628
2689
  rpcUrl?: string;
2629
2690
  }): Promise<SubaccountRecoveryResult>;
2691
+ /**
2692
+ * Merge a subaccount's entire private balance back to the main wallet.
2693
+ *
2694
+ * Builds a ZK transfer proof that moves every unspent UTXO belonging to the
2695
+ * child keypair into a new UTXO encrypted to the parent (root) keypair,
2696
+ * then submits it via the relay.
2697
+ *
2698
+ * @param options - Merge options
2699
+ * @returns Merge result with transaction hash and amount
2700
+ *
2701
+ * @example
2702
+ * ```typescript
2703
+ * const result = await mergeSubaccount({
2704
+ * rootPrivateKey: process.env.VEIL_KEY as `0x${string}`,
2705
+ * slot: 0,
2706
+ * pool: 'eth',
2707
+ * });
2708
+ * console.log(`Merged ${result.amount} — tx: ${result.transactionHash}`);
2709
+ * ```
2710
+ */
2711
+ declare function mergeSubaccount(options: SubaccountMergeOptions): Promise<SubaccountMergeResult>;
2630
2712
 
2631
2713
  /**
2632
2714
  * Crypto utilities for Veil SDK
@@ -2697,4 +2779,4 @@ declare function getExtDataHash(extData: ExtDataInput): bigint;
2697
2779
  */
2698
2780
  declare function shuffle<T>(array: T[]): T[];
2699
2781
 
2700
- export { ADDRESSES, type BuildTransferProofOptions, type BuildWithdrawProofOptions, CIRCUIT_CONFIG, type DepositTxOptions, ENTRY_ABI, ERC20_ABI, type EncryptedMessage, type ExtData, type ExtDataInput, FIELD_SIZE, FORWARDER_ABI, FORWARDER_CONTRACT_VERSION, FORWARDER_FACTORY_ABI, Keypair, MAX_SUBACCOUNT_SLOTS, MERKLE_TREE_HEIGHT, type MessageSigner, type NetworkAddresses, POOL_ABI, POOL_CONFIG, type PendingDeposit, type PoolConfig, type PrepareTransactionParams, type PrivateBalanceResult, type ProgressCallback, type ProofArgs, type ProofBuildResult, type ProofInput, QUEUE_ABI, type QueueBalanceResult, type RegisterTxOptions, RelayError, type RelayErrorResponse, type RelayExtData, type RelayMetadata, type RelayPool, type RelayProofArgs, type RelayRequest, type RelayResponse, type RelayType, type SubaccountAsset, type SubaccountAssetBalance, type SubaccountBalances, type SubaccountDeployRequest, type SubaccountQueueStatus, type SubaccountRecoveryResult, type SubaccountRelayResult, type SubaccountSlot, type SubaccountStatusResult, type SubaccountSweepRequest, type SubaccountWithdrawTypedData, type SubmitRelayOptions, type Token, type TransactionData, type TransactionResult, type TransferResult, Utxo, type UtxoInfo, type UtxoParams, type UtxoSelectionResult, VEIL_SIGNED_MESSAGE, type WithdrawResult, buildApproveUSDCTx, buildChangeDepositKeyTx, buildDepositETHTx, buildDepositTx, buildDepositUSDCTx, buildMerkleTree, buildRegisterTx, buildSubaccountRecoveryTx, buildSubaccountWithdrawTypedData, buildTransferProof, buildWithdrawProof, checkRecipientRegistration, checkRelayHealth, deploySubaccountForwarder, deriveSubaccountChildDepositKey, deriveSubaccountChildOwner, deriveSubaccountChildPrivateKey, deriveSubaccountSalt, deriveSubaccountSlot, findNextSubaccountWithdrawNonce, getAddresses, getExtDataHash, getForwarderFactoryAddress, getMerklePath, getPoolAddress, getPrivateBalance, getQueueAddress, getQueueBalance, getRelayInfo, getRelayUrl, getSubaccountStatus, isSubaccountForwarderDeployed, isSubaccountWithdrawNonceUsed, mergeUtxos, packEncryptedMessage, poseidonHash, poseidonHash2, predictSubaccountForwarder, prepareTransaction, prove, randomBN, selectCircuit, selectUtxosForWithdraw, shuffle, signSubaccountWithdraw, submitRelay, sweepSubaccountForwarder, toBuffer, toFixedHex, transfer, unpackEncryptedMessage, withdraw };
2782
+ export { ADDRESSES, type BuildTransferProofOptions, type BuildWithdrawProofOptions, CIRCUIT_CONFIG, type DepositTxOptions, ENTRY_ABI, ERC20_ABI, type EncryptedMessage, type ExtData, type ExtDataInput, FIELD_SIZE, FORWARDER_ABI, FORWARDER_CONTRACT_VERSION, FORWARDER_FACTORY_ABI, Keypair, MAX_SUBACCOUNT_SLOTS, MERKLE_TREE_HEIGHT, type MessageSigner, type NetworkAddresses, POOL_ABI, POOL_CONFIG, type PendingDeposit, type PoolConfig, type PrepareTransactionParams, type PrivateBalanceResult, type ProgressCallback, type ProofArgs, type ProofBuildResult, type ProofInput, QUEUE_ABI, type QueueBalanceResult, type RegisterTxOptions, RelayError, type RelayErrorResponse, type RelayExtData, type RelayMetadata, type RelayPool, type RelayProofArgs, type RelayRequest, type RelayResponse, type RelayType, type SubaccountAsset, type SubaccountAssetBalance, type SubaccountBalances, type SubaccountDeployRequest, type SubaccountMergeOptions, type SubaccountMergeResult, type SubaccountPrivateBalanceStatus, type SubaccountPrivateBalances, type SubaccountQueueStatus, type SubaccountRecoveryResult, type SubaccountRelayResult, type SubaccountSlot, type SubaccountStatusResult, type SubaccountSweepRequest, type SubaccountWithdrawTypedData, type SubmitRelayOptions, type Token, type TransactionData, type TransactionResult, type TransferResult, Utxo, type UtxoInfo, type UtxoParams, type UtxoSelectionResult, VEIL_SIGNED_MESSAGE, type WithdrawResult, buildApproveUSDCTx, buildChangeDepositKeyTx, buildDepositETHTx, buildDepositTx, buildDepositUSDCTx, buildMerkleTree, buildRegisterTx, buildSubaccountRecoveryTx, buildSubaccountWithdrawTypedData, buildTransferProof, buildWithdrawProof, checkRecipientRegistration, checkRelayHealth, deploySubaccountForwarder, deriveSubaccountChildDepositKey, deriveSubaccountChildOwner, deriveSubaccountChildPrivateKey, deriveSubaccountSalt, deriveSubaccountSlot, findNextSubaccountWithdrawNonce, getAddresses, getExtDataHash, getForwarderFactoryAddress, getMerklePath, getPoolAddress, getPrivateBalance, getQueueAddress, getQueueBalance, getRelayInfo, getRelayUrl, getSubaccountPrivateBalance, getSubaccountStatus, isSubaccountForwarderDeployed, isSubaccountWithdrawNonceUsed, mergeSubaccount, mergeUtxos, packEncryptedMessage, poseidonHash, poseidonHash2, predictSubaccountForwarder, prepareTransaction, prove, randomBN, selectCircuit, selectUtxosForWithdraw, shuffle, signSubaccountWithdraw, submitRelay, sweepSubaccountForwarder, toBuffer, toFixedHex, transfer, unpackEncryptedMessage, withdraw };
package/dist/index.js CHANGED
@@ -1641,7 +1641,16 @@ async function postRelayJson(endpoint, body, relayUrl) {
1641
1641
  },
1642
1642
  body: JSON.stringify(body)
1643
1643
  });
1644
- const data = await response.json();
1644
+ const text = await response.text();
1645
+ let data;
1646
+ try {
1647
+ data = JSON.parse(text);
1648
+ } catch {
1649
+ throw new RelayError(
1650
+ `Relay returned non-JSON response (HTTP ${response.status})`,
1651
+ response.status
1652
+ );
1653
+ }
1645
1654
  if (!response.ok) {
1646
1655
  const errorData = data;
1647
1656
  throw new RelayError(
@@ -2213,11 +2222,12 @@ function deriveSubaccountChildDepositKey(childPrivateKey) {
2213
2222
  }
2214
2223
  async function predictSubaccountForwarder(options) {
2215
2224
  const publicClient = createBaseClient(options.rpcUrl);
2225
+ const depositKeyBytes = options.childDepositKey.startsWith("0x") ? options.childDepositKey : `0x${options.childDepositKey}`;
2216
2226
  return publicClient.readContract({
2217
2227
  abi: FORWARDER_FACTORY_ABI,
2218
2228
  address: getForwarderFactoryAddress(),
2219
2229
  functionName: "computeAddress",
2220
- args: [options.salt, options.childDepositKey, options.childOwner]
2230
+ args: [options.salt, depositKeyBytes, options.childOwner]
2221
2231
  });
2222
2232
  }
2223
2233
  async function deriveSubaccountSlot(options) {
@@ -2254,7 +2264,7 @@ async function deploySubaccountForwarder(options) {
2254
2264
  slot: options.slot,
2255
2265
  rpcUrl: options.rpcUrl
2256
2266
  });
2257
- return postRelayJson(
2267
+ const result = await postRelayJson(
2258
2268
  "/stealth/deploy",
2259
2269
  {
2260
2270
  salt: slot.salt,
@@ -2264,6 +2274,7 @@ async function deploySubaccountForwarder(options) {
2264
2274
  },
2265
2275
  options.relayUrl
2266
2276
  );
2277
+ return { ...result, slot };
2267
2278
  }
2268
2279
  async function sweepSubaccountForwarder(options) {
2269
2280
  const asset = normalizeAsset(options.asset);
@@ -2288,11 +2299,32 @@ function toQueueStatus(asset, result) {
2288
2299
  pendingDeposits: result.pendingDeposits
2289
2300
  };
2290
2301
  }
2302
+ function toPrivateBalanceStatus(result) {
2303
+ return {
2304
+ privateBalance: result.privateBalance,
2305
+ privateBalanceWei: result.privateBalanceWei,
2306
+ utxoCount: result.utxoCount,
2307
+ spentCount: result.spentCount,
2308
+ unspentCount: result.unspentCount
2309
+ };
2310
+ }
2311
+ async function getSubaccountPrivateBalance(options) {
2312
+ const normalizedSlot = normalizeSlot(options.slot);
2313
+ assertPrivateKey(options.rootPrivateKey, "rootPrivateKey");
2314
+ const childPrivateKey = deriveSubaccountChildPrivateKey(options.rootPrivateKey, normalizedSlot);
2315
+ const childKeypair = new Keypair(childPrivateKey);
2316
+ return getPrivateBalance({
2317
+ keypair: childKeypair,
2318
+ pool: options.pool,
2319
+ rpcUrl: options.rpcUrl,
2320
+ onProgress: options.onProgress
2321
+ });
2322
+ }
2291
2323
  async function getSubaccountStatus(options) {
2292
2324
  const slot = await deriveSubaccountSlot(options);
2293
2325
  const publicClient = createBaseClient(options.rpcUrl);
2294
2326
  const addresses = getAddresses();
2295
- const [deployed, ethWei, usdcWei, ethQueue, usdcQueue] = await Promise.all([
2327
+ const [deployed, ethWei, usdcWei, ethQueue, usdcQueue, ethPrivate, usdcPrivate] = await Promise.all([
2296
2328
  isSubaccountForwarderDeployed({
2297
2329
  forwarderAddress: slot.forwarderAddress,
2298
2330
  rpcUrl: options.rpcUrl
@@ -2313,6 +2345,18 @@ async function getSubaccountStatus(options) {
2313
2345
  address: slot.forwarderAddress,
2314
2346
  pool: "usdc",
2315
2347
  rpcUrl: options.rpcUrl
2348
+ }),
2349
+ getSubaccountPrivateBalance({
2350
+ rootPrivateKey: options.rootPrivateKey,
2351
+ slot: options.slot,
2352
+ pool: "eth",
2353
+ rpcUrl: options.rpcUrl
2354
+ }),
2355
+ getSubaccountPrivateBalance({
2356
+ rootPrivateKey: options.rootPrivateKey,
2357
+ slot: options.slot,
2358
+ pool: "usdc",
2359
+ rpcUrl: options.rpcUrl
2316
2360
  })
2317
2361
  ]);
2318
2362
  return {
@@ -2328,6 +2372,10 @@ async function getSubaccountStatus(options) {
2328
2372
  balanceWei: usdcWei.toString()
2329
2373
  }
2330
2374
  },
2375
+ privateBalances: {
2376
+ eth: toPrivateBalanceStatus(ethPrivate),
2377
+ usdc: toPrivateBalanceStatus(usdcPrivate)
2378
+ },
2331
2379
  queues: {
2332
2380
  eth: toQueueStatus("eth", ethQueue),
2333
2381
  usdc: toQueueStatus("usdc", usdcQueue)
@@ -2474,7 +2522,138 @@ async function buildSubaccountRecoveryTx(options) {
2474
2522
  signature
2475
2523
  };
2476
2524
  }
2525
+ async function mergeSubaccount(options) {
2526
+ const {
2527
+ rootPrivateKey,
2528
+ slot,
2529
+ pool = "eth",
2530
+ rpcUrl,
2531
+ relayUrl,
2532
+ onProgress
2533
+ } = options;
2534
+ const normalizedSlot = normalizeSlot(slot);
2535
+ assertPrivateKey(rootPrivateKey, "rootPrivateKey");
2536
+ const poolConfig = POOL_CONFIG[pool];
2537
+ const poolAddress = getPoolAddress(pool);
2538
+ const childPrivateKey = deriveSubaccountChildPrivateKey(rootPrivateKey, normalizedSlot);
2539
+ const childKeypair = new Keypair(childPrivateKey);
2540
+ const parentKeypair = new Keypair(rootPrivateKey);
2541
+ onProgress?.("Fetching subaccount balance...");
2542
+ const balanceResult = await getPrivateBalance({
2543
+ keypair: childKeypair,
2544
+ pool,
2545
+ rpcUrl,
2546
+ onProgress
2547
+ });
2548
+ const unspentUtxoInfos = balanceResult.utxos.filter((u) => !u.isSpent);
2549
+ if (unspentUtxoInfos.length === 0) {
2550
+ throw new Error("Subaccount has no unspent UTXOs to merge");
2551
+ }
2552
+ if (unspentUtxoInfos.length > 16) {
2553
+ throw new Error(
2554
+ `Subaccount has ${unspentUtxoInfos.length} unspent UTXOs which exceeds the 16-input circuit limit. Consolidate UTXOs on the subaccount first before merging.`
2555
+ );
2556
+ }
2557
+ onProgress?.("Preparing UTXOs...");
2558
+ const publicClient = createPublicClient({
2559
+ chain: base,
2560
+ transport: http(rpcUrl)
2561
+ });
2562
+ const utxos = [];
2563
+ for (const utxoInfo of unspentUtxoInfos) {
2564
+ const encryptedOutputs = await publicClient.readContract({
2565
+ address: poolAddress,
2566
+ abi: POOL_ABI,
2567
+ functionName: "getEncryptedOutputs",
2568
+ args: [BigInt(utxoInfo.index), BigInt(utxoInfo.index + 1)]
2569
+ });
2570
+ if (encryptedOutputs.length > 0) {
2571
+ try {
2572
+ const utxo = Utxo.decrypt(encryptedOutputs[0], childKeypair);
2573
+ utxo.index = utxoInfo.index;
2574
+ utxos.push(utxo);
2575
+ } catch {
2576
+ }
2577
+ }
2578
+ }
2579
+ if (utxos.length === 0) {
2580
+ throw new Error("Failed to decrypt subaccount UTXOs");
2581
+ }
2582
+ onProgress?.("Selecting UTXOs...");
2583
+ const amount = balanceResult.privateBalance;
2584
+ const { selectedUtxos, changeAmount } = selectUtxosForWithdraw(
2585
+ utxos,
2586
+ amount,
2587
+ poolConfig.decimals
2588
+ );
2589
+ const outputs = [];
2590
+ const mergeWei = parseUnits(amount, poolConfig.decimals);
2591
+ outputs.push(new Utxo({ amount: mergeWei, keypair: parentKeypair }));
2592
+ if (changeAmount > 0n) {
2593
+ outputs.push(new Utxo({ amount: changeAmount, keypair: parentKeypair }));
2594
+ }
2595
+ onProgress?.("Fetching commitments...");
2596
+ const nextIndex = await publicClient.readContract({
2597
+ address: poolAddress,
2598
+ abi: POOL_ABI,
2599
+ functionName: "nextIndex"
2600
+ });
2601
+ const BATCH_SIZE = 5e3;
2602
+ const commitments = [];
2603
+ const totalBatches = Math.ceil(nextIndex / BATCH_SIZE);
2604
+ for (let start = 0; start < nextIndex; start += BATCH_SIZE) {
2605
+ const end = Math.min(start + BATCH_SIZE, nextIndex);
2606
+ const batchNum = Math.floor(start / BATCH_SIZE) + 1;
2607
+ onProgress?.("Fetching commitments", `batch ${batchNum}/${totalBatches}`);
2608
+ const batch = await publicClient.readContract({
2609
+ address: poolAddress,
2610
+ abi: POOL_ABI,
2611
+ functionName: "getCommitments",
2612
+ args: [BigInt(start), BigInt(end)]
2613
+ });
2614
+ commitments.push(...batch.map((c) => c.toString()));
2615
+ }
2616
+ onProgress?.("Building ZK proof...");
2617
+ const result = await prepareTransaction({
2618
+ commitments,
2619
+ inputs: selectedUtxos,
2620
+ outputs,
2621
+ fee: 0,
2622
+ recipient: "0x0000000000000000000000000000000000000000",
2623
+ relayer: "0x0000000000000000000000000000000000000000",
2624
+ onProgress
2625
+ });
2626
+ onProgress?.("Submitting to relay...");
2627
+ const relayResult = await submitRelay({
2628
+ type: "transfer",
2629
+ pool,
2630
+ relayUrl,
2631
+ proofArgs: {
2632
+ proof: result.args.proof,
2633
+ root: result.args.root,
2634
+ inputNullifiers: result.args.inputNullifiers,
2635
+ outputCommitments: result.args.outputCommitments,
2636
+ publicAmount: result.args.publicAmount,
2637
+ extDataHash: result.args.extDataHash
2638
+ },
2639
+ extData: result.extData,
2640
+ metadata: {
2641
+ amount,
2642
+ recipient: "self",
2643
+ inputUtxoCount: selectedUtxos.length,
2644
+ outputUtxoCount: outputs.length
2645
+ }
2646
+ });
2647
+ return {
2648
+ success: relayResult.success,
2649
+ transactionHash: relayResult.transactionHash,
2650
+ blockNumber: relayResult.blockNumber,
2651
+ amount,
2652
+ slot: normalizedSlot,
2653
+ pool
2654
+ };
2655
+ }
2477
2656
 
2478
- export { ADDRESSES, CIRCUIT_CONFIG, ENTRY_ABI, ERC20_ABI, FIELD_SIZE, FORWARDER_ABI, FORWARDER_CONTRACT_VERSION, FORWARDER_FACTORY_ABI, Keypair, MAX_SUBACCOUNT_SLOTS, MERKLE_TREE_HEIGHT, POOL_ABI, POOL_CONFIG, QUEUE_ABI, RelayError, Utxo, VEIL_SIGNED_MESSAGE, buildApproveUSDCTx, buildChangeDepositKeyTx, buildDepositETHTx, buildDepositTx, buildDepositUSDCTx, buildMerkleTree, buildRegisterTx, buildSubaccountRecoveryTx, buildSubaccountWithdrawTypedData, buildTransferProof, buildWithdrawProof, checkRecipientRegistration, checkRelayHealth, deploySubaccountForwarder, deriveSubaccountChildDepositKey, deriveSubaccountChildOwner, deriveSubaccountChildPrivateKey, deriveSubaccountSalt, deriveSubaccountSlot, findNextSubaccountWithdrawNonce, getAddresses, getExtDataHash, getForwarderFactoryAddress, getMerklePath, getPoolAddress, getPrivateBalance, getQueueAddress, getQueueBalance, getRelayInfo, getRelayUrl, getSubaccountStatus, isSubaccountForwarderDeployed, isSubaccountWithdrawNonceUsed, mergeUtxos, packEncryptedMessage, poseidonHash, poseidonHash2, predictSubaccountForwarder, prepareTransaction, prove, randomBN, selectCircuit, selectUtxosForWithdraw, shuffle, signSubaccountWithdraw, submitRelay, sweepSubaccountForwarder, toBuffer, toFixedHex, transfer, unpackEncryptedMessage, withdraw };
2657
+ export { ADDRESSES, CIRCUIT_CONFIG, ENTRY_ABI, ERC20_ABI, FIELD_SIZE, FORWARDER_ABI, FORWARDER_CONTRACT_VERSION, FORWARDER_FACTORY_ABI, Keypair, MAX_SUBACCOUNT_SLOTS, MERKLE_TREE_HEIGHT, POOL_ABI, POOL_CONFIG, QUEUE_ABI, RelayError, Utxo, VEIL_SIGNED_MESSAGE, buildApproveUSDCTx, buildChangeDepositKeyTx, buildDepositETHTx, buildDepositTx, buildDepositUSDCTx, buildMerkleTree, buildRegisterTx, buildSubaccountRecoveryTx, buildSubaccountWithdrawTypedData, buildTransferProof, buildWithdrawProof, checkRecipientRegistration, checkRelayHealth, deploySubaccountForwarder, deriveSubaccountChildDepositKey, deriveSubaccountChildOwner, deriveSubaccountChildPrivateKey, deriveSubaccountSalt, deriveSubaccountSlot, findNextSubaccountWithdrawNonce, getAddresses, getExtDataHash, getForwarderFactoryAddress, getMerklePath, getPoolAddress, getPrivateBalance, getQueueAddress, getQueueBalance, getRelayInfo, getRelayUrl, getSubaccountPrivateBalance, getSubaccountStatus, isSubaccountForwarderDeployed, isSubaccountWithdrawNonceUsed, mergeSubaccount, mergeUtxos, packEncryptedMessage, poseidonHash, poseidonHash2, predictSubaccountForwarder, prepareTransaction, prove, randomBN, selectCircuit, selectUtxosForWithdraw, shuffle, signSubaccountWithdraw, submitRelay, sweepSubaccountForwarder, toBuffer, toFixedHex, transfer, unpackEncryptedMessage, withdraw };
2479
2658
  //# sourceMappingURL=index.js.map
2480
2659
  //# sourceMappingURL=index.js.map