dipcoin-cli 0.5.0 → 0.8.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.
Files changed (48) hide show
  1. package/README.md +215 -543
  2. package/dist/cli/commands/account.d.ts.map +1 -1
  3. package/dist/cli/commands/balance.d.ts.map +1 -1
  4. package/dist/cli/commands/history.d.ts.map +1 -1
  5. package/dist/cli/commands/market.d.ts.map +1 -1
  6. package/dist/cli/commands/orders.d.ts +3 -0
  7. package/dist/cli/commands/orders.d.ts.map +1 -0
  8. package/dist/cli/commands/position.d.ts.map +1 -1
  9. package/dist/cli/commands/sub-account.d.ts +3 -0
  10. package/dist/cli/commands/sub-account.d.ts.map +1 -0
  11. package/dist/cli/commands/trade.d.ts.map +1 -1
  12. package/dist/cli/commands/vault.d.ts +0 -1
  13. package/dist/cli/commands/vault.d.ts.map +1 -1
  14. package/dist/cli/index.d.ts +1 -1
  15. package/dist/cli/index.d.ts.map +1 -1
  16. package/dist/cli/utils/output.d.ts +8 -0
  17. package/dist/cli/utils/output.d.ts.map +1 -1
  18. package/dist/cli/utils/sdk-factory.d.ts +35 -2
  19. package/dist/cli/utils/sdk-factory.d.ts.map +1 -1
  20. package/dist/cli/utils/vault-index.d.ts +6 -0
  21. package/dist/cli/utils/vault-index.d.ts.map +1 -0
  22. package/dist/cli.cjs +1521 -258
  23. package/dist/cli.cjs.map +1 -0
  24. package/dist/config/deployed/testnet/main_contract.json +58 -275
  25. package/dist/index.cjs.js +2 -2
  26. package/dist/index.cjs.js.map +1 -1
  27. package/dist/index.esm.js +2 -2
  28. package/dist/index.esm.js.map +1 -1
  29. package/dist/index.mjs +2 -2
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/src/config/index.d.ts.map +1 -1
  32. package/dist/src/constants/index.d.ts +1 -0
  33. package/dist/src/constants/index.d.ts.map +1 -1
  34. package/dist/src/onchain/exchange.d.ts +41 -0
  35. package/dist/src/onchain/exchange.d.ts.map +1 -0
  36. package/dist/src/onchain/index.d.ts +5 -0
  37. package/dist/src/onchain/index.d.ts.map +1 -0
  38. package/dist/src/onchain/order-signer.d.ts +17 -0
  39. package/dist/src/onchain/order-signer.d.ts.map +1 -0
  40. package/dist/src/onchain/transaction-builder.d.ts +46 -0
  41. package/dist/src/onchain/transaction-builder.d.ts.map +1 -0
  42. package/dist/src/sdk/sdk.d.ts +117 -4
  43. package/dist/src/sdk/sdk.d.ts.map +1 -1
  44. package/dist/src/types/index.d.ts +6 -1
  45. package/dist/src/types/index.d.ts.map +1 -1
  46. package/dist/src/utils/jwt-cache.d.ts +14 -0
  47. package/dist/src/utils/jwt-cache.d.ts.map +1 -0
  48. package/package.json +6 -10
package/dist/cli.cjs CHANGED
@@ -1,27 +1,30 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
+ Object.defineProperty(exports, '__esModule', { value: true });
5
+
4
6
  var commander = require('commander');
5
7
  var dotenv = require('dotenv');
6
- var perpTsLibrary = require('@dipcoinlab/perp-ts-library');
8
+ var path = require('path');
9
+ var os = require('os');
10
+ var fs = require('fs');
11
+ var ed25519 = require('@mysten/sui/keypairs/ed25519');
7
12
  var transactions = require('@mysten/sui/transactions');
8
13
  var BigNumber = require('bignumber.js');
9
14
  var pythSuiJs = require('@pythnetwork/pyth-sui-js');
10
15
  var child_process = require('child_process');
11
- var ed25519 = require('@mysten/sui/keypairs/ed25519');
12
16
  var utils = require('@mysten/sui/utils');
13
17
  var buffer = require('buffer');
14
- var fs = require('fs');
15
- var path = require('path');
16
18
  var url = require('url');
17
19
  var Table = require('cli-table3');
18
20
 
19
21
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
22
 
21
23
  var dotenv__default = /*#__PURE__*/_interopDefaultLegacy(dotenv);
22
- var BigNumber__default = /*#__PURE__*/_interopDefaultLegacy(BigNumber);
23
- var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
24
24
  var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
25
+ var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
26
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
27
+ var BigNumber__default = /*#__PURE__*/_interopDefaultLegacy(BigNumber);
25
28
  var Table__default = /*#__PURE__*/_interopDefaultLegacy(Table);
26
29
 
27
30
  // Copyright (c) 2025 Dipcoin LLC
@@ -41,6 +44,7 @@ function initSDKOptions(options) {
41
44
  apiBaseUrl: options.apiBaseUrl || DEFAULT_API_URLS[options.network],
42
45
  network: options.network,
43
46
  customRpc: options.customRpc,
47
+ subAccountKey: options.subAccountKey,
44
48
  };
45
49
  }
46
50
 
@@ -5012,7 +5016,7 @@ function asciiToBase16(ch) {
5012
5016
  * Convert hex string to byte array. Uses built-in function, when available.
5013
5017
  * @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])
5014
5018
  */
5015
- function hexToBytes(hex) {
5019
+ function hexToBytes$1(hex) {
5016
5020
  if (typeof hex !== 'string')
5017
5021
  throw new Error('hex string expected, got ' + typeof hex);
5018
5022
  // @ts-ignore
@@ -9366,6 +9370,7 @@ const API_ENDPOINTS = {
9366
9370
  FUNDING_SETTLEMENTS: "/api/perp-trade-api/history/funding-settlements",
9367
9371
  BALANCE_CHANGES: "/api/perp-trade-api/history/balance-changes",
9368
9372
  ORACLE: "/api/perp-market-api/oracle",
9373
+ VAULTS_BY_CREATOR: "/api/perp-vault-api/public/vaults/by-creator",
9369
9374
  };
9370
9375
  /**
9371
9376
  * Onboarding message for authentication
@@ -9492,6 +9497,322 @@ class HttpClient {
9492
9497
  }
9493
9498
  }
9494
9499
 
9500
+ // Copyright (c) 2025 Dipcoin LLC
9501
+ // SPDX-License-Identifier: Apache-2.0
9502
+ function computeOrderFlag(order) {
9503
+ let flag = 0;
9504
+ if (order.ioc)
9505
+ flag += 1;
9506
+ if (order.postOnly)
9507
+ flag += 2;
9508
+ if (order.reduceOnly)
9509
+ flag += 4;
9510
+ if (order.isLong)
9511
+ flag += 8;
9512
+ if (order.orderbookOnly)
9513
+ flag += 16;
9514
+ return flag;
9515
+ }
9516
+ function packPayload(fields) {
9517
+ let result = "{\n";
9518
+ for (const [key, value] of fields) {
9519
+ result += `"${key}":"${value}",\n`;
9520
+ }
9521
+ result += '"domain":"dipcoin.io"\n';
9522
+ result += "}";
9523
+ return result;
9524
+ }
9525
+ function getOrderMessageForUIWallet(order) {
9526
+ const flag = computeOrderFlag(order);
9527
+ const fields = [
9528
+ ["market", `${order.market}`],
9529
+ ["creator", `${order.creator}`],
9530
+ ["isLong", `${order.isLong}`],
9531
+ ["reduceOnly", `${order.reduceOnly}`],
9532
+ ["postOnly", `${order.postOnly}`],
9533
+ ["orderbookOnly", `${order.orderbookOnly}`],
9534
+ ["ioc", `${order.ioc || false}`],
9535
+ ["quantity", `${order.quantity.toFixed(0)}`],
9536
+ ["price", `${order.price.toFixed(0)}`],
9537
+ ["leverage", `${order.leverage.toFixed(0)}`],
9538
+ ["expiration", `${order.expiration.toFixed(0)}`],
9539
+ ["salt", `${order.salt.toFixed(0)}`],
9540
+ ["orderFlag", `${flag}`],
9541
+ ];
9542
+ return packPayload(fields);
9543
+ }
9544
+
9545
+ // Copyright (c) 2025 Dipcoin LLC
9546
+ const CLOCK_OBJECT_ID = "0x6";
9547
+ // ---------------------------------------------------------------------------
9548
+ // Deployment config helpers
9549
+ // ---------------------------------------------------------------------------
9550
+ function getPackageId(deployment) {
9551
+ const pkgs = deployment?.packages;
9552
+ if (!pkgs || !pkgs.length)
9553
+ throw new Error("Deployment config missing packages array");
9554
+ return pkgs[pkgs.length - 1];
9555
+ }
9556
+ function getProtocolConfigId(deployment) {
9557
+ const id = deployment?.objects?.ProtocolConfig?.id;
9558
+ if (!id)
9559
+ throw new Error("Deployment config missing ProtocolConfig id");
9560
+ return id;
9561
+ }
9562
+ function getBankId(deployment) {
9563
+ const id = deployment?.objects?.Bank?.id;
9564
+ if (!id)
9565
+ throw new Error("Deployment config missing Bank id");
9566
+ return id;
9567
+ }
9568
+ function getSubAccountsId(deployment) {
9569
+ const id = deployment?.objects?.SubAccounts?.id;
9570
+ if (!id)
9571
+ throw new Error("Deployment config missing SubAccounts id");
9572
+ return id;
9573
+ }
9574
+ function getTxIndexerId(deployment) {
9575
+ const id = deployment?.objects?.TxIndexer?.id;
9576
+ if (!id)
9577
+ throw new Error("Deployment config missing TxIndexer id");
9578
+ return id;
9579
+ }
9580
+ function getCurrencyType(deployment) {
9581
+ const dt = deployment?.objects?.Currency?.dataType;
9582
+ if (!dt)
9583
+ throw new Error("Deployment config missing Currency dataType");
9584
+ return dt;
9585
+ }
9586
+ function getPerpetualId(deployment, market) {
9587
+ const m = market?.toUpperCase();
9588
+ const id = deployment?.markets?.[m]?.Objects?.Perpetual?.id;
9589
+ if (!id)
9590
+ throw new Error(`Deployment config missing Perpetual id for ${m}`);
9591
+ return id;
9592
+ }
9593
+ function getPriceOracleObjectId(deployment, market) {
9594
+ const m = market?.toUpperCase() || "ETH-PERP";
9595
+ const id = deployment?.markets?.[m]?.Objects?.PriceInfoObject?.id;
9596
+ if (!id)
9597
+ throw new Error(`Deployment config missing PriceInfoObject id for ${m}`);
9598
+ return id;
9599
+ }
9600
+ function getPriceOracleFeedId(deployment, market) {
9601
+ const m = market?.toUpperCase() || "ETH-PERP";
9602
+ const feedId = deployment?.markets?.[m]?.Config?.priceInfoFeedId;
9603
+ if (!feedId)
9604
+ throw new Error(`Deployment config missing priceInfoFeedId for ${m}`);
9605
+ return feedId;
9606
+ }
9607
+ function getPythPkgId(deployment, market) {
9608
+ const m = market?.toUpperCase() || "ETH-PERP";
9609
+ const dataType = deployment?.markets?.[m]?.Objects?.PriceInfoObject?.dataType;
9610
+ if (!dataType)
9611
+ throw new Error(`Deployment config missing PriceInfoObject dataType for ${m}`);
9612
+ return dataType.split("::")[0];
9613
+ }
9614
+ function hexToBytes(hex) {
9615
+ const h = hex.startsWith("0x") ? hex.slice(2) : hex;
9616
+ const bytes = [];
9617
+ for (let i = 0; i < h.length; i += 2) {
9618
+ bytes.push(parseInt(h.substr(i, 2), 16));
9619
+ }
9620
+ return bytes;
9621
+ }
9622
+ function toBigIntStr(value, decimals) {
9623
+ // Equivalent to library's J(value, decimals) → multiply by 10^decimals and return integer string
9624
+ const factor = Math.pow(10, decimals);
9625
+ return Math.round(value * factor).toString();
9626
+ }
9627
+ // ---------------------------------------------------------------------------
9628
+ // Transaction builders
9629
+ // ---------------------------------------------------------------------------
9630
+ function buildAddMarginTx(deployment, args, tx, gasBudget, sender) {
9631
+ const t = tx || new transactions.Transaction();
9632
+ if (gasBudget)
9633
+ t.setGasBudget(gasBudget);
9634
+ if (sender)
9635
+ t.setSender(sender);
9636
+ const packageId = getPackageId(deployment);
9637
+ t.moveCall({
9638
+ target: `${packageId}::exchange::add_margin_v2`,
9639
+ arguments: [
9640
+ t.object(getProtocolConfigId(deployment)),
9641
+ t.object(CLOCK_OBJECT_ID),
9642
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
9643
+ t.object(getBankId(deployment)),
9644
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
9645
+ t.object(getTxIndexerId(deployment)),
9646
+ t.object(getPriceOracleObjectId(deployment, args.market)),
9647
+ t.pure.address(args.account || sender || ""),
9648
+ t.pure.u128(args.amount.toString()),
9649
+ ],
9650
+ typeArguments: [getCurrencyType(deployment)],
9651
+ });
9652
+ return t;
9653
+ }
9654
+ function buildRemoveMarginTx(deployment, args, tx, gasBudget, sender) {
9655
+ const t = tx || new transactions.Transaction();
9656
+ if (gasBudget)
9657
+ t.setGasBudget(gasBudget);
9658
+ if (sender)
9659
+ t.setSender(sender);
9660
+ const packageId = getPackageId(deployment);
9661
+ t.moveCall({
9662
+ target: `${packageId}::exchange::remove_margin_v2`,
9663
+ arguments: [
9664
+ t.object(getProtocolConfigId(deployment)),
9665
+ t.object(CLOCK_OBJECT_ID),
9666
+ t.object(args.perpID || getPerpetualId(deployment, args.market)),
9667
+ t.object(getBankId(deployment)),
9668
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
9669
+ t.object(getTxIndexerId(deployment)),
9670
+ t.object(getPriceOracleObjectId(deployment, args.market)),
9671
+ t.pure.address(args.account || sender || ""),
9672
+ t.pure.u128(args.amount.toString()),
9673
+ ],
9674
+ typeArguments: [getCurrencyType(deployment)],
9675
+ });
9676
+ return t;
9677
+ }
9678
+ function buildDepositTx(deployment, args, tx, gasBudget, sender) {
9679
+ const t = tx || new transactions.Transaction();
9680
+ if (gasBudget)
9681
+ t.setGasBudget(gasBudget);
9682
+ if (sender)
9683
+ t.setSender(sender);
9684
+ // The library uses a coin-splitting intent for deposit.
9685
+ // We replicate the same Move call structure: bank::deposit_v2
9686
+ // with a SplitCoins + the amount.
9687
+ const coinType = getCurrencyType(deployment);
9688
+ const coin = t.splitCoins(t.gas, [BigInt(args.amount)]);
9689
+ const packageId = getPackageId(deployment);
9690
+ t.moveCall({
9691
+ target: `${packageId}::bank::deposit_v2`,
9692
+ arguments: [
9693
+ t.object(getProtocolConfigId(deployment)),
9694
+ t.object(args.bankID || getBankId(deployment)),
9695
+ t.object(getTxIndexerId(deployment)),
9696
+ t.pure.address(args.accountAddress || sender || ""),
9697
+ t.pure.u64(args.amount),
9698
+ coin,
9699
+ ],
9700
+ typeArguments: [coinType],
9701
+ });
9702
+ return t;
9703
+ }
9704
+ function buildWithdrawTx(deployment, args, tx, gasBudget, sender) {
9705
+ const t = tx || new transactions.Transaction();
9706
+ if (gasBudget)
9707
+ t.setGasBudget(gasBudget);
9708
+ if (sender)
9709
+ t.setSender(sender);
9710
+ const packageId = getPackageId(deployment);
9711
+ t.moveCall({
9712
+ target: `${packageId}::bank::withdraw_v2`,
9713
+ arguments: [
9714
+ t.object(getProtocolConfigId(deployment)),
9715
+ t.object(args.bankID || getBankId(deployment)),
9716
+ t.object(getTxIndexerId(deployment)),
9717
+ t.pure.address(args.accountAddress || sender || ""),
9718
+ t.pure.u128(args.amount),
9719
+ ],
9720
+ typeArguments: [getCurrencyType(deployment)],
9721
+ });
9722
+ return t;
9723
+ }
9724
+ function buildSetSubAccountTx(deployment, args, tx, gasBudget, sender) {
9725
+ const t = tx || new transactions.Transaction();
9726
+ if (gasBudget)
9727
+ t.setGasBudget(gasBudget);
9728
+ if (sender)
9729
+ t.setSender(sender);
9730
+ const packageId = getPackageId(deployment);
9731
+ t.moveCall({
9732
+ target: `${packageId}::sub_accounts::set_sub_account`,
9733
+ arguments: [
9734
+ t.object(getProtocolConfigId(deployment)),
9735
+ t.object(args.subAccountsMapID || getSubAccountsId(deployment)),
9736
+ t.pure.address(args.account),
9737
+ t.pure.bool(args.status),
9738
+ ],
9739
+ typeArguments: [],
9740
+ });
9741
+ return t;
9742
+ }
9743
+ function buildSetOraclePriceTx(deployment, args, tx, gasBudget, sender) {
9744
+ const t = tx || new transactions.Transaction();
9745
+ if (gasBudget)
9746
+ t.setGasBudget(gasBudget);
9747
+ if (sender)
9748
+ t.setSender(sender);
9749
+ const pythPkgId = getPythPkgId(deployment, args.market);
9750
+ const oracleObjId = getPriceOracleObjectId(deployment, args.market);
9751
+ const feedIdHex = getPriceOracleFeedId(deployment, args.market);
9752
+ const feedIdBytes = hexToBytes(feedIdHex);
9753
+ const priceU64 = toBigIntStr(args.price, 6);
9754
+ t.moveCall({
9755
+ target: `${pythPkgId}::price_info::update_price_info_object_for_test`,
9756
+ arguments: [
9757
+ t.object(oracleObjId),
9758
+ t.object(CLOCK_OBJECT_ID),
9759
+ t.pure.u64(priceU64),
9760
+ t.pure.u64(args.confidence || "10"),
9761
+ t.pure(suiBcs.vector(suiBcs.u8()).serialize(new Uint8Array(feedIdBytes))),
9762
+ ],
9763
+ typeArguments: [],
9764
+ });
9765
+ return t;
9766
+ }
9767
+ function buildBatchSetOraclePriceTx(deployment, args, tx, gasBudget, sender) {
9768
+ let t = tx || new transactions.Transaction();
9769
+ for (const priceArgs of args.prices) {
9770
+ t = buildSetOraclePriceTx(deployment, priceArgs, t, undefined, undefined);
9771
+ }
9772
+ if (gasBudget)
9773
+ t.setGasBudget(gasBudget);
9774
+ if (sender)
9775
+ t.setSender(sender);
9776
+ return t;
9777
+ }
9778
+
9779
+ // Copyright (c) 2025 Dipcoin LLC
9780
+ async function executeTxBlock(suiClient, tx, signer) {
9781
+ return suiClient.signAndExecuteTransaction({
9782
+ signer,
9783
+ transaction: tx,
9784
+ options: {
9785
+ showObjectChanges: true,
9786
+ showEffects: true,
9787
+ showEvents: true,
9788
+ showInput: true,
9789
+ },
9790
+ });
9791
+ }
9792
+ function getDeploymentPerpetualID(deployment, market) {
9793
+ return getPerpetualId(deployment, market);
9794
+ }
9795
+ async function getOraclePrice(suiClient, deployment, market) {
9796
+ const objId = getPriceOracleObjectId(deployment, market);
9797
+ const obj = await suiClient.getObject({ id: objId, options: { showContent: true } });
9798
+ const fields = obj.data?.content?.fields?.price_info?.fields?.price_feed?.fields?.price?.fields;
9799
+ if (!fields)
9800
+ throw new Error(`Failed to read oracle price for ${market}`);
9801
+ return Number(fields.price.fields.magnitude) / Math.pow(10, Number(fields.expo.fields.magnitude));
9802
+ }
9803
+ async function depositToBank(suiClient, deployment, args, signer) {
9804
+ const tx = buildDepositTx(deployment, args, undefined, args.gasBudget, signer.getPublicKey().toSuiAddress());
9805
+ return executeTxBlock(suiClient, tx, signer);
9806
+ }
9807
+ async function withdrawFromBank(suiClient, deployment, args, signer) {
9808
+ const tx = buildWithdrawTx(deployment, args, undefined, args.gasBudget, signer.getPublicKey().toSuiAddress());
9809
+ return executeTxBlock(suiClient, tx, signer);
9810
+ }
9811
+ async function setSubAccount(suiClient, deployment, args, signer) {
9812
+ const tx = buildSetSubAccountTx(deployment, args, undefined, args.gasBudget, signer.getPublicKey().toSuiAddress());
9813
+ return executeTxBlock(suiClient, tx, signer);
9814
+ }
9815
+
9495
9816
  // Copyright (c) 2025 Dipcoin LLC
9496
9817
  // SPDX-License-Identifier: Apache-2.0
9497
9818
  /**
@@ -9929,7 +10250,7 @@ function bytesToNumberLE(bytes) {
9929
10250
  return hexToNumber(bytesToHex(Uint8Array.from(bytes).reverse()));
9930
10251
  }
9931
10252
  function numberToBytesBE(n, len) {
9932
- return hexToBytes(n.toString(16).padStart(len * 2, '0'));
10253
+ return hexToBytes$1(n.toString(16).padStart(len * 2, '0'));
9933
10254
  }
9934
10255
  function numberToBytesLE(n, len) {
9935
10256
  return numberToBytesBE(n, len).reverse();
@@ -9947,7 +10268,7 @@ function ensureBytes(title, hex, expectedLength) {
9947
10268
  let res;
9948
10269
  if (typeof hex === 'string') {
9949
10270
  try {
9950
- res = hexToBytes(hex);
10271
+ res = hexToBytes$1(hex);
9951
10272
  }
9952
10273
  catch (e) {
9953
10274
  throw new Error(title + ' must be hex string or Uint8Array, cause: ' + e);
@@ -11693,7 +12014,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
11693
12014
  throw new Error('invalid format');
11694
12015
  }
11695
12016
  static fromHex(hex, format) {
11696
- return this.fromBytes(hexToBytes(hex), format);
12017
+ return this.fromBytes(hexToBytes$1(hex), format);
11697
12018
  }
11698
12019
  addRecoveryBit(recovery) {
11699
12020
  return new Signature(this.r, this.s, recovery);
@@ -11742,7 +12063,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
11742
12063
  if (format === 'compact')
11743
12064
  return concatBytes(Fn.toBytes(this.r), Fn.toBytes(this.s));
11744
12065
  if (format === 'der')
11745
- return hexToBytes(DER.hexFromSig(this));
12066
+ return hexToBytes$1(DER.hexFromSig(this));
11746
12067
  throw new Error('invalid format');
11747
12068
  }
11748
12069
  toHex(format) {
@@ -12375,7 +12696,7 @@ function bytesToNumber(bytes) {
12375
12696
  function numberToBytes(num) {
12376
12697
  if (typeof num !== 'bigint')
12377
12698
  throw new Error('bigint expected');
12378
- return hexToBytes(num.toString(16).padStart(64, '0'));
12699
+ return hexToBytes$1(num.toString(16).padStart(64, '0'));
12379
12700
  }
12380
12701
  const MASTER_SECRET = utf8ToBytes('Bitcoin seed');
12381
12702
  // Bitcoin hardcoded by default
@@ -15318,7 +15639,7 @@ function findFirstNonZeroIndex(bytes) {
15318
15639
  }
15319
15640
  function toPaddedBigEndianBytes(num, width) {
15320
15641
  const hex = num.toString(16);
15321
- return hexToBytes(hex.padStart(width * 2, "0").slice(-width * 2));
15642
+ return hexToBytes$1(hex.padStart(width * 2, "0").slice(-width * 2));
15322
15643
  }
15323
15644
  function toBigEndianBytes(num, width) {
15324
15645
  const bytes = toPaddedBigEndianBytes(num, width);
@@ -16449,7 +16770,7 @@ function findPackageRoot(startDir) {
16449
16770
  if (fs__default["default"].existsSync(packageJsonPath)) {
16450
16771
  try {
16451
16772
  const packageJson = JSON.parse(fs__default["default"].readFileSync(packageJsonPath, "utf-8"));
16452
- if (packageJson.name === "@dipcoinlab/perp-client-ts") {
16773
+ if (packageJson.name === "dipcoin-cli") {
16453
16774
  return currentDir;
16454
16775
  }
16455
16776
  }
@@ -16514,6 +16835,78 @@ function readFile(filePath) {
16514
16835
  return config;
16515
16836
  }
16516
16837
 
16838
+ // Copyright (c) 2025 Dipcoin LLC
16839
+ const CACHE_DIR = path__default["default"].join(os__default["default"].homedir(), ".config", "dipcoin", "jwt");
16840
+ /** Safety margin: treat token as expired 60s before actual expiry */
16841
+ const EXPIRY_MARGIN_S = 60;
16842
+ function getCachePath(address) {
16843
+ return path__default["default"].join(CACHE_DIR, `${address}.json`);
16844
+ }
16845
+ function parseJwtExp(token) {
16846
+ try {
16847
+ const parts = token.split(".");
16848
+ if (parts.length !== 3)
16849
+ return null;
16850
+ const payload = JSON.parse(Buffer.from(parts[1], "base64").toString());
16851
+ return typeof payload.exp === "number" ? payload.exp : null;
16852
+ }
16853
+ catch {
16854
+ return null;
16855
+ }
16856
+ }
16857
+ /**
16858
+ * Load a cached JWT for the given wallet address.
16859
+ * Returns the token string if valid and not expired, otherwise null.
16860
+ */
16861
+ function loadCachedJwt(address) {
16862
+ try {
16863
+ const filePath = getCachePath(address);
16864
+ if (!fs__default["default"].existsSync(filePath))
16865
+ return null;
16866
+ const raw = fs__default["default"].readFileSync(filePath, "utf-8");
16867
+ const entry = JSON.parse(raw);
16868
+ const now = Math.floor(Date.now() / 1000);
16869
+ if (entry.exp - EXPIRY_MARGIN_S > now) {
16870
+ return entry.token;
16871
+ }
16872
+ // Expired, clean up
16873
+ fs__default["default"].unlinkSync(filePath);
16874
+ return null;
16875
+ }
16876
+ catch {
16877
+ return null;
16878
+ }
16879
+ }
16880
+ /**
16881
+ * Save a JWT to the local cache.
16882
+ */
16883
+ function saveCachedJwt(address, token) {
16884
+ try {
16885
+ const exp = parseJwtExp(token);
16886
+ if (!exp)
16887
+ return;
16888
+ fs__default["default"].mkdirSync(CACHE_DIR, { recursive: true });
16889
+ const entry = { token, exp };
16890
+ fs__default["default"].writeFileSync(getCachePath(address), JSON.stringify(entry), { mode: 0o600 });
16891
+ }
16892
+ catch {
16893
+ // Non-fatal: cache write failure is acceptable
16894
+ }
16895
+ }
16896
+ /**
16897
+ * Remove cached JWT for the given address.
16898
+ */
16899
+ function clearCachedJwt(address) {
16900
+ try {
16901
+ const filePath = getCachePath(address);
16902
+ if (fs__default["default"].existsSync(filePath))
16903
+ fs__default["default"].unlinkSync(filePath);
16904
+ }
16905
+ catch {
16906
+ // ignore
16907
+ }
16908
+ }
16909
+
16517
16910
  // Copyright (c) 2025 Dipcoin LLC
16518
16911
  /**
16519
16912
  * DipCoin Perpetual Trading SDK
@@ -16529,10 +16922,8 @@ class DipCoinPerpSDK {
16529
16922
  subJwtToken;
16530
16923
  isAuthenticating = false;
16531
16924
  isSubAuthenticating = false;
16532
- exchangeOnChain;
16533
16925
  deploymentConfig;
16534
16926
  suiClient;
16535
- transactionBuilder;
16536
16927
  priceServiceConnection;
16537
16928
  pythClient;
16538
16929
  tradingPairsCache;
@@ -16554,7 +16945,12 @@ class DipCoinPerpSDK {
16554
16945
  }
16555
16946
  // Initialize sub-account keypair if provided
16556
16947
  if (options.subAccountKey) {
16557
- this.subKeypair = fromExportedKeypair(options.subAccountKey);
16948
+ if (typeof options.subAccountKey === "string") {
16949
+ this.subKeypair = fromExportedKeypair(options.subAccountKey);
16950
+ }
16951
+ else {
16952
+ this.subKeypair = options.subAccountKey;
16953
+ }
16558
16954
  this.subAddress = this.subKeypair.getPublicKey().toSuiAddress();
16559
16955
  }
16560
16956
  // Get wallet address
@@ -16563,10 +16959,6 @@ class DipCoinPerpSDK {
16563
16959
  this.deploymentConfig = readFile(`config/deployed/${options.network}/main_contract.json`);
16564
16960
  const rpcUrl = options.customRpc || getFullnodeUrl(options.network);
16565
16961
  this.suiClient = new SuiJsonRpcClient({ url: rpcUrl });
16566
- this.exchangeOnChain = new perpTsLibrary.ExchangeOnChain(this.deploymentConfig, this.suiClient, this.keypair);
16567
- const packageId = this.getDeploymentPackageId();
16568
- const protocolConfigId = this.getDeploymentProtocolConfigId();
16569
- this.transactionBuilder = new perpTsLibrary.TransactionBuilder(packageId, protocolConfigId, this.deploymentConfig, this.suiClient);
16570
16962
  }
16571
16963
  /**
16572
16964
  * Get wallet address
@@ -16593,30 +16985,27 @@ class DipCoinPerpSDK {
16593
16985
  */
16594
16986
  async authenticate() {
16595
16987
  try {
16596
- // If already authenticated and token exists, return it
16988
+ // If already authenticated in this process, return it
16597
16989
  if (this.jwtToken) {
16598
- return {
16599
- status: true,
16600
- data: this.jwtToken,
16601
- };
16990
+ return { status: true, data: this.jwtToken };
16991
+ }
16992
+ // Try loading from local cache
16993
+ const cached = loadCachedJwt(this.walletAddress);
16994
+ if (cached) {
16995
+ this.jwtToken = cached;
16996
+ this.httpClient.setAuthToken(cached);
16997
+ return { status: true, data: cached };
16602
16998
  }
16603
16999
  // Prevent concurrent authentication requests
16604
17000
  if (this.isAuthenticating) {
16605
- // Wait for ongoing authentication
16606
17001
  await new Promise((resolve) => setTimeout(resolve, 100));
16607
17002
  if (this.jwtToken) {
16608
- return {
16609
- status: true,
16610
- data: this.jwtToken,
16611
- };
17003
+ return { status: true, data: this.jwtToken };
16612
17004
  }
16613
17005
  }
16614
17006
  this.isAuthenticating = true;
16615
- // 1. Prepare onboarding message
16616
17007
  const messageBytes = new TextEncoder().encode(ONBOARDING_MESSAGE);
16617
- // 2. Sign the message
16618
17008
  const signature = await signMessage(this.keypair, messageBytes);
16619
- // 3. Call authorize endpoint to get JWT token
16620
17009
  const response = await this.httpClient.post(API_ENDPOINTS.AUTHORIZE, {
16621
17010
  userAddress: this.walletAddress,
16622
17011
  isTermAccepted: true,
@@ -16625,26 +17014,18 @@ class DipCoinPerpSDK {
16625
17014
  if (response.code === 200 && response.data?.token) {
16626
17015
  this.jwtToken = response.data.token;
16627
17016
  this.httpClient.setAuthToken(this.jwtToken);
17017
+ saveCachedJwt(this.walletAddress, this.jwtToken);
16628
17018
  this.isAuthenticating = false;
16629
- return {
16630
- status: true,
16631
- data: this.jwtToken,
16632
- };
17019
+ return { status: true, data: this.jwtToken };
16633
17020
  }
16634
17021
  else {
16635
17022
  this.isAuthenticating = false;
16636
- return {
16637
- status: false,
16638
- error: response.message || "Failed to authenticate",
16639
- };
17023
+ return { status: false, error: response.message || "Failed to authenticate" };
16640
17024
  }
16641
17025
  }
16642
17026
  catch (error) {
16643
17027
  this.isAuthenticating = false;
16644
- return {
16645
- status: false,
16646
- error: formatError(error),
16647
- };
17028
+ return { status: false, error: formatError(error) };
16648
17029
  }
16649
17030
  }
16650
17031
  /**
@@ -16655,10 +17036,17 @@ class DipCoinPerpSDK {
16655
17036
  if (!this.subKeypair) {
16656
17037
  return { status: false, error: "Sub-account keypair not configured" };
16657
17038
  }
17039
+ const subAddress = this.subKeypair.getPublicKey().toSuiAddress();
16658
17040
  try {
16659
17041
  if (this.subJwtToken) {
16660
17042
  return { status: true, data: this.subJwtToken };
16661
17043
  }
17044
+ // Try loading from local cache
17045
+ const cached = loadCachedJwt(subAddress);
17046
+ if (cached) {
17047
+ this.subJwtToken = cached;
17048
+ return { status: true, data: cached };
17049
+ }
16662
17050
  if (this.isSubAuthenticating) {
16663
17051
  await new Promise((resolve) => setTimeout(resolve, 100));
16664
17052
  if (this.subJwtToken) {
@@ -16668,7 +17056,6 @@ class DipCoinPerpSDK {
16668
17056
  this.isSubAuthenticating = true;
16669
17057
  const messageBytes = new TextEncoder().encode(ONBOARDING_MESSAGE);
16670
17058
  const signature = await signMessage(this.subKeypair, messageBytes);
16671
- const subAddress = this.subKeypair.getPublicKey().toSuiAddress();
16672
17059
  const response = await this.httpClient.post(API_ENDPOINTS.AUTHORIZE, {
16673
17060
  userAddress: subAddress,
16674
17061
  isTermAccepted: true,
@@ -16676,6 +17063,7 @@ class DipCoinPerpSDK {
16676
17063
  });
16677
17064
  if (response.code === 200 && response.data?.token) {
16678
17065
  this.subJwtToken = response.data.token;
17066
+ saveCachedJwt(subAddress, this.subJwtToken);
16679
17067
  this.isSubAuthenticating = false;
16680
17068
  return { status: true, data: this.subJwtToken };
16681
17069
  }
@@ -16732,6 +17120,10 @@ class DipCoinPerpSDK {
16732
17120
  * Clear JWT token (logout)
16733
17121
  */
16734
17122
  clearAuth() {
17123
+ if (this.jwtToken)
17124
+ clearCachedJwt(this.walletAddress);
17125
+ if (this.subJwtToken && this.subAddress)
17126
+ clearCachedJwt(this.subAddress);
16735
17127
  this.jwtToken = undefined;
16736
17128
  this.subJwtToken = undefined;
16737
17129
  this.httpClient.setAuthToken("");
@@ -16840,21 +17232,21 @@ class DipCoinPerpSDK {
16840
17232
  };
16841
17233
  }
16842
17234
  // Generate order message for signing
16843
- const orderMsg = perpTsLibrary.OrderSigner.getOrderMessageForUIWallet(order);
17235
+ const orderMsg = getOrderMessageForUIWallet(order);
16844
17236
  const orderHashBytes = new TextEncoder().encode(orderMsg);
16845
17237
  // Sign main order
16846
17238
  const orderSignature = await signMessage(signingKeypair, orderHashBytes);
16847
17239
  // Sign TP order if exists
16848
17240
  let tpOrderSignature;
16849
17241
  if (tpOrder) {
16850
- const tpOrderMsg = perpTsLibrary.OrderSigner.getOrderMessageForUIWallet(tpOrder);
17242
+ const tpOrderMsg = getOrderMessageForUIWallet(tpOrder);
16851
17243
  const tpOrderHashBytes = new TextEncoder().encode(tpOrderMsg);
16852
17244
  tpOrderSignature = await signMessage(signingKeypair, tpOrderHashBytes);
16853
17245
  }
16854
17246
  // Sign SL order if exists
16855
17247
  let slOrderSignature;
16856
17248
  if (slOrder) {
16857
- const slOrderMsg = perpTsLibrary.OrderSigner.getOrderMessageForUIWallet(slOrder);
17249
+ const slOrderMsg = getOrderMessageForUIWallet(slOrder);
16858
17250
  const slOrderHashBytes = new TextEncoder().encode(slOrderMsg);
16859
17251
  slOrderSignature = await signMessage(signingKeypair, slOrderHashBytes);
16860
17252
  }
@@ -17231,7 +17623,7 @@ class DipCoinPerpSDK {
17231
17623
  const tpOrder = {
17232
17624
  market,
17233
17625
  creator: this.walletAddress,
17234
- isLong,
17626
+ isLong: !isLong,
17235
17627
  reduceOnly,
17236
17628
  postOnly,
17237
17629
  orderbookOnly,
@@ -17242,7 +17634,7 @@ class DipCoinPerpSDK {
17242
17634
  expiration: expirationBN,
17243
17635
  salt: tpSaltValue,
17244
17636
  };
17245
- const tpOrderMsg = perpTsLibrary.OrderSigner.getOrderMessageForUIWallet(tpOrder);
17637
+ const tpOrderMsg = getOrderMessageForUIWallet(tpOrder);
17246
17638
  const tpOrderSignature = await signMessage(signingKeypair, new TextEncoder().encode(tpOrderMsg));
17247
17639
  tpPayload = {
17248
17640
  ...planPayloadBase,
@@ -17276,7 +17668,7 @@ class DipCoinPerpSDK {
17276
17668
  const slOrder = {
17277
17669
  market,
17278
17670
  creator: this.walletAddress,
17279
- isLong,
17671
+ isLong: !isLong,
17280
17672
  reduceOnly,
17281
17673
  postOnly,
17282
17674
  orderbookOnly,
@@ -17287,7 +17679,7 @@ class DipCoinPerpSDK {
17287
17679
  expiration: expirationBN,
17288
17680
  salt: slSaltValue,
17289
17681
  };
17290
- const slOrderMsg = perpTsLibrary.OrderSigner.getOrderMessageForUIWallet(slOrder);
17682
+ const slOrderMsg = getOrderMessageForUIWallet(slOrder);
17291
17683
  const slOrderSignature = await signMessage(signingKeypair, new TextEncoder().encode(slOrderMsg));
17292
17684
  slPayload = {
17293
17685
  ...planPayloadBase,
@@ -17552,36 +17944,7 @@ class DipCoinPerpSDK {
17552
17944
  */
17553
17945
  async getTradingPairs() {
17554
17946
  try {
17555
- // Ensure authenticated before making request
17556
- const authResult = await this.authenticate();
17557
- if (!authResult.status) {
17558
- return {
17559
- status: false,
17560
- error: authResult.error || "Authentication failed",
17561
- };
17562
- }
17563
17947
  const response = await this.httpClient.get(API_ENDPOINTS.GET_TRADING_PAIRS);
17564
- // Handle JWT expiration
17565
- if (response.code === 1000) {
17566
- this.clearAuth();
17567
- const retryAuth = await this.authenticate();
17568
- if (retryAuth.status) {
17569
- const retryResponse = await this.httpClient.get(API_ENDPOINTS.GET_TRADING_PAIRS);
17570
- if (retryResponse.code === 200) {
17571
- const pairs = Array.isArray(retryResponse.data)
17572
- ? retryResponse.data
17573
- : retryResponse.data?.data || [];
17574
- return {
17575
- status: true,
17576
- data: pairs,
17577
- };
17578
- }
17579
- }
17580
- return {
17581
- status: false,
17582
- error: "Authentication expired and refresh failed",
17583
- };
17584
- }
17585
17948
  if (response.code === 200) {
17586
17949
  const pairs = Array.isArray(response.data) ? response.data : response.data?.data || [];
17587
17950
  return {
@@ -17715,12 +18078,6 @@ class DipCoinPerpSDK {
17715
18078
  error: "Symbol is required",
17716
18079
  };
17717
18080
  }
17718
- // Market API endpoints typically don't require authentication
17719
- // But we'll try to authenticate if possible for consistency
17720
- // If authentication fails, we'll still try to fetch the order book
17721
- await this.authenticate().catch(() => {
17722
- // Ignore authentication errors for market data
17723
- });
17724
18081
  const params = {
17725
18082
  symbol,
17726
18083
  };
@@ -17873,12 +18230,6 @@ class DipCoinPerpSDK {
17873
18230
  error: "Symbol is required",
17874
18231
  };
17875
18232
  }
17876
- // Market API endpoints typically don't require authentication
17877
- // But we'll try to authenticate if possible for consistency
17878
- // If authentication fails, we'll still try to fetch the ticker
17879
- await this.authenticate().catch(() => {
17880
- // Ignore authentication errors for market data
17881
- });
17882
18233
  const params = {
17883
18234
  symbol,
17884
18235
  };
@@ -18078,6 +18429,10 @@ class DipCoinPerpSDK {
18078
18429
  queryParams.pageSize = params.pageSize;
18079
18430
  if (params?.parentAddress)
18080
18431
  queryParams.parentAddress = params.parentAddress;
18432
+ if (params?.beginTime)
18433
+ queryParams.beginTime = params.beginTime;
18434
+ if (params?.endTime)
18435
+ queryParams.endTime = params.endTime;
18081
18436
  let response = await this.httpClient.get(API_ENDPOINTS.HISTORY_ORDERS, { params: queryParams });
18082
18437
  if (response.code === 1000) {
18083
18438
  this.clearAuth();
@@ -18117,6 +18472,8 @@ class DipCoinPerpSDK {
18117
18472
  queryParams.pageSize = params.pageSize;
18118
18473
  if (params?.parentAddress)
18119
18474
  queryParams.parentAddress = params.parentAddress;
18475
+ if (params?.beginTime)
18476
+ queryParams.beginTime = params.beginTime;
18120
18477
  let response = await this.httpClient.get(API_ENDPOINTS.FUNDING_SETTLEMENTS, { params: queryParams });
18121
18478
  if (response.code === 1000) {
18122
18479
  this.clearAuth();
@@ -18154,6 +18511,8 @@ class DipCoinPerpSDK {
18154
18511
  queryParams.pageSize = params.pageSize;
18155
18512
  if (params?.parentAddress)
18156
18513
  queryParams.parentAddress = params.parentAddress;
18514
+ if (params?.beginTime)
18515
+ queryParams.beginTime = params.beginTime;
18157
18516
  let response = await this.httpClient.get(API_ENDPOINTS.BALANCE_CHANGES, { params: queryParams });
18158
18517
  if (response.code === 1000) {
18159
18518
  this.clearAuth();
@@ -18184,7 +18543,6 @@ class DipCoinPerpSDK {
18184
18543
  if (!symbol) {
18185
18544
  return { status: false, error: "Symbol is required" };
18186
18545
  }
18187
- await this.authenticate().catch(() => { });
18188
18546
  const response = await this.httpClient.get(API_ENDPOINTS.ORACLE, { params: { symbol } });
18189
18547
  if (response.code === 200 && response.data !== undefined) {
18190
18548
  return { status: true, data: String(response.data) };
@@ -18202,10 +18560,11 @@ class DipCoinPerpSDK {
18202
18560
  async addMargin(params) {
18203
18561
  const transaction = await this.buildMarginTransaction(params, "add");
18204
18562
  if (transaction) {
18205
- return this.exchangeOnChain.executeTxBlock(transaction, this.keypair);
18563
+ return executeTxBlock(this.suiClient, transaction, this.keypair);
18206
18564
  }
18207
18565
  const fallbackPayload = this.buildMarginCallArgs(params, "add");
18208
- return this.exchangeOnChain.addMargin(fallbackPayload);
18566
+ const tx = buildAddMarginTx(this.deploymentConfig, fallbackPayload, undefined, fallbackPayload.gasBudget, this.keypair.getPublicKey().toSuiAddress());
18567
+ return executeTxBlock(this.suiClient, tx, this.keypair);
18209
18568
  }
18210
18569
  /**
18211
18570
  * Remove isolated margin from an existing position (on-chain)
@@ -18214,10 +18573,11 @@ class DipCoinPerpSDK {
18214
18573
  async removeMargin(params) {
18215
18574
  const transaction = await this.buildMarginTransaction(params, "remove");
18216
18575
  if (transaction) {
18217
- return this.exchangeOnChain.executeTxBlock(transaction, this.keypair);
18576
+ return executeTxBlock(this.suiClient, transaction, this.keypair);
18218
18577
  }
18219
18578
  const fallbackPayload = this.buildMarginCallArgs(params, "remove");
18220
- return this.exchangeOnChain.removeMargin(fallbackPayload);
18579
+ const tx = buildRemoveMarginTx(this.deploymentConfig, fallbackPayload, undefined, fallbackPayload.gasBudget, this.keypair.getPublicKey().toSuiAddress());
18580
+ return executeTxBlock(this.suiClient, tx, this.keypair);
18221
18581
  }
18222
18582
  /**
18223
18583
  * Build ExchangeOnChain call args for margin adjustments
@@ -18247,13 +18607,6 @@ class DipCoinPerpSDK {
18247
18607
  txHash,
18248
18608
  };
18249
18609
  }
18250
- getDeploymentPackageId() {
18251
- const packages = this.deploymentConfig?.packages;
18252
- if (!packages || !packages.length) {
18253
- throw new Error("Deployment config missing packages array");
18254
- }
18255
- return packages[packages.length - 1];
18256
- }
18257
18610
  getDeploymentProtocolConfigId() {
18258
18611
  const protocolId = this.deploymentConfig?.objects?.ProtocolConfig?.id;
18259
18612
  if (!protocolId) {
@@ -18262,18 +18615,15 @@ class DipCoinPerpSDK {
18262
18615
  return protocolId;
18263
18616
  }
18264
18617
  async buildMarginTransaction(params, action) {
18265
- if (!this.transactionBuilder) {
18266
- return undefined;
18267
- }
18268
18618
  const payload = this.buildMarginCallArgs(params, action);
18269
18619
  const updatePriceTx = payload.market
18270
18620
  ? await this.buildUpdatePriceTransaction(payload.market)
18271
18621
  : undefined;
18272
18622
  const baseTx = updatePriceTx || new transactions.Transaction();
18273
18623
  if (action === "add") {
18274
- return this.transactionBuilder.exchange_addMarginTx(payload, baseTx, params.gasBudget);
18624
+ return buildAddMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
18275
18625
  }
18276
- return this.transactionBuilder.exchange_removeMarginTx(payload, baseTx, params.gasBudget);
18626
+ return buildRemoveMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
18277
18627
  }
18278
18628
  async buildUpdatePriceTransaction(symbol) {
18279
18629
  if (!symbol) {
@@ -18316,11 +18666,11 @@ class DipCoinPerpSDK {
18316
18666
  }
18317
18667
  async buildTestnetPriceUpdateTransaction(symbol) {
18318
18668
  try {
18319
- const oraclePrice = await this.exchangeOnChain.getOraclePrice(symbol);
18669
+ const oraclePrice = await getOraclePrice(this.suiClient, this.deploymentConfig, symbol);
18320
18670
  if (oraclePrice === undefined || oraclePrice === null) {
18321
18671
  return undefined;
18322
18672
  }
18323
- return this.transactionBuilder.price_info_setOraclePriceTx({
18673
+ return buildSetOraclePriceTx(this.deploymentConfig, {
18324
18674
  price: Number(oraclePrice),
18325
18675
  market: symbol,
18326
18676
  });
@@ -18415,7 +18765,7 @@ class DipCoinPerpSDK {
18415
18765
  */
18416
18766
  resolvePerpIdFromDeployment(market) {
18417
18767
  try {
18418
- const perpId = this.exchangeOnChain.getPerpetualID(market);
18768
+ const perpId = getDeploymentPerpetualID(this.deploymentConfig, market);
18419
18769
  return perpId || undefined;
18420
18770
  }
18421
18771
  catch (error) {
@@ -18429,7 +18779,7 @@ class DipCoinPerpSDK {
18429
18779
  * @returns On-chain transaction result
18430
18780
  */
18431
18781
  async setSubAccount(subAddress) {
18432
- return await this.exchangeOnChain.setSubAccount({
18782
+ return await setSubAccount(this.suiClient, this.deploymentConfig, {
18433
18783
  account: subAddress,
18434
18784
  status: true,
18435
18785
  }, this.keypair);
@@ -18445,7 +18795,7 @@ class DipCoinPerpSDK {
18445
18795
  * ```
18446
18796
  */
18447
18797
  async depositToBank(amount) {
18448
- return await this.exchangeOnChain.depositToBank({
18798
+ return await depositToBank(this.suiClient, this.deploymentConfig, {
18449
18799
  amount: formatNormalToWei(amount, DECIMALS.USDC),
18450
18800
  accountAddress: this.address,
18451
18801
  }, this.keypair);
@@ -18461,7 +18811,7 @@ class DipCoinPerpSDK {
18461
18811
  * ```
18462
18812
  */
18463
18813
  async withdrawFromBank(amount) {
18464
- return await this.exchangeOnChain.withdrawFromBank({
18814
+ return await withdrawFromBank(this.suiClient, this.deploymentConfig, {
18465
18815
  amount: formatNormalToWei(amount, DECIMALS.USDC),
18466
18816
  accountAddress: this.address,
18467
18817
  }, this.keypair);
@@ -18470,9 +18820,9 @@ class DipCoinPerpSDK {
18470
18820
  * Get all coin balances on-chain for the current wallet address
18471
18821
  * @returns Array of coin balances with coinType and totalBalance
18472
18822
  */
18473
- async getAllBalances() {
18823
+ async getAllBalances(owner) {
18474
18824
  try {
18475
- const balances = await this.suiClient.getAllBalances({ owner: this.walletAddress });
18825
+ const balances = await this.suiClient.getAllBalances({ owner: owner || this.walletAddress });
18476
18826
  return { status: true, data: balances };
18477
18827
  }
18478
18828
  catch (error) {
@@ -18495,6 +18845,416 @@ class DipCoinPerpSDK {
18495
18845
  return { status: false, error: formatError(error) };
18496
18846
  }
18497
18847
  }
18848
+ // =====================================================
18849
+ // Vault (on-chain contract) Operations
18850
+ // =====================================================
18851
+ //
18852
+ // Vault functions live in a SEPARATE package from the exchange.
18853
+ // The library's ExchangeOnChain uses getPackageID() (exchange pkg)
18854
+ // for vault moveCall targets, causing TypeMismatch errors.
18855
+ // We build vault transactions manually with the correct vault package.
18856
+ // =====================================================
18857
+ getVaultPackageId() {
18858
+ const pkg = this.deploymentConfig?.vaultPackage;
18859
+ if (!pkg)
18860
+ throw new Error("Deployment config missing vaultPackage");
18861
+ return pkg;
18862
+ }
18863
+ getVaultConfigId() {
18864
+ const id = this.deploymentConfig?.objects?.VaultConfig?.id;
18865
+ if (!id)
18866
+ throw new Error("Deployment config missing VaultConfig id");
18867
+ return id;
18868
+ }
18869
+ getBankId() {
18870
+ const id = this.deploymentConfig?.objects?.Bank?.id;
18871
+ if (!id)
18872
+ throw new Error("Deployment config missing Bank id");
18873
+ return id;
18874
+ }
18875
+ getTxIndexerId() {
18876
+ const id = this.deploymentConfig?.objects?.TxIndexer?.id;
18877
+ if (!id)
18878
+ throw new Error("Deployment config missing TxIndexer id");
18879
+ return id;
18880
+ }
18881
+ getSubAccountsId() {
18882
+ const id = this.deploymentConfig?.objects?.SubAccounts?.id;
18883
+ if (!id)
18884
+ throw new Error("Deployment config missing SubAccounts id");
18885
+ return id;
18886
+ }
18887
+ getCurrencyType() {
18888
+ const dt = this.deploymentConfig?.objects?.Currency?.dataType;
18889
+ if (!dt)
18890
+ throw new Error("Deployment config missing Currency dataType");
18891
+ return dt;
18892
+ }
18893
+ getMarketSymbols() {
18894
+ return Object.keys(this.deploymentConfig?.markets || {});
18895
+ }
18896
+ getVaultPerpetualId(symbol) {
18897
+ const id = this.deploymentConfig?.markets?.[symbol]?.Objects?.Perpetual?.id;
18898
+ if (!id)
18899
+ throw new Error(`Deployment config missing Perpetual id for ${symbol}`);
18900
+ return id;
18901
+ }
18902
+ getVaultPriceInfoObjectId(symbol) {
18903
+ const id = this.deploymentConfig?.markets?.[symbol]?.Objects?.PriceInfoObject?.id;
18904
+ if (!id)
18905
+ throw new Error(`Deployment config missing PriceInfoObject id for ${symbol}`);
18906
+ return id;
18907
+ }
18908
+ async buildVaultNavTransaction(vaultID, markets, tx) {
18909
+ const t = tx || new transactions.Transaction();
18910
+ const vaultPkg = this.getVaultPackageId();
18911
+ const currencyType = this.getCurrencyType();
18912
+ const symbols = markets && markets.length > 0 ? markets : this.getMarketSymbols();
18913
+ // On testnet, prepend price oracle updates so PriceInfoObjects are fresh
18914
+ if (this.options.network === "testnet") {
18915
+ const prices = [];
18916
+ for (const sym of symbols) {
18917
+ try {
18918
+ const oraclePrice = await getOraclePrice(this.suiClient, this.deploymentConfig, sym);
18919
+ if (oraclePrice !== undefined && oraclePrice !== null) {
18920
+ prices.push({ price: Number(oraclePrice), market: sym });
18921
+ }
18922
+ }
18923
+ catch {
18924
+ // skip symbols where oracle price is unavailable
18925
+ }
18926
+ }
18927
+ if (prices.length > 0) {
18928
+ buildBatchSetOraclePriceTx(this.deploymentConfig, { prices }, t);
18929
+ }
18930
+ }
18931
+ const [nav] = t.moveCall({
18932
+ target: `${vaultPkg}::vault::new_vault_nav`,
18933
+ arguments: [
18934
+ t.object(this.getDeploymentProtocolConfigId()),
18935
+ t.object(this.getVaultConfigId()),
18936
+ t.object(vaultID),
18937
+ t.object(this.getBankId()),
18938
+ ],
18939
+ typeArguments: [currencyType],
18940
+ });
18941
+ for (const sym of symbols) {
18942
+ const perpId = this.getVaultPerpetualId(sym);
18943
+ const priceInfoId = this.getVaultPriceInfoObjectId(sym);
18944
+ t.moveCall({
18945
+ target: `${vaultPkg}::vault::compute_perpetual_position_value`,
18946
+ arguments: [nav, t.object("0x6"), t.object(perpId), t.object(priceInfoId)],
18947
+ typeArguments: [],
18948
+ });
18949
+ }
18950
+ return { nav, tx: t };
18951
+ }
18952
+ async signAndExecuteVaultTx(tx) {
18953
+ tx.setSender(this.keypair.getPublicKey().toSuiAddress());
18954
+ return await this.suiClient.signAndExecuteTransaction({
18955
+ signer: this.keypair,
18956
+ transaction: tx,
18957
+ options: { showEffects: true, showObjectChanges: true },
18958
+ });
18959
+ }
18960
+ /**
18961
+ * Create a new on-chain vault
18962
+ */
18963
+ async createVault(args) {
18964
+ const tx = new transactions.Transaction();
18965
+ const vaultPkg = this.getVaultPackageId();
18966
+ tx.moveCall({
18967
+ target: `${vaultPkg}::vault::create_vault`,
18968
+ arguments: [
18969
+ tx.object(this.getDeploymentProtocolConfigId()),
18970
+ tx.object("0x6"),
18971
+ tx.object(this.getVaultConfigId()),
18972
+ tx.object(this.getSubAccountsId()),
18973
+ tx.object(this.getBankId()),
18974
+ tx.object(this.getTxIndexerId()),
18975
+ tx.pure.string(args.name),
18976
+ tx.pure.address(args.trader),
18977
+ tx.pure.u128(formatNormalToWei(args.maxCap)),
18978
+ tx.pure.u128(formatNormalToWei(args.minDepositAmount)),
18979
+ tx.pure.u128(args.creatorMinimumShareRatio),
18980
+ tx.pure.u128(args.creatorProfitShareRatio),
18981
+ tx.pure.u128(formatNormalToWei(args.initialAmount)),
18982
+ ],
18983
+ typeArguments: [this.getCurrencyType()],
18984
+ });
18985
+ return this.signAndExecuteVaultTx(tx);
18986
+ }
18987
+ /**
18988
+ * Deposit USDC into a vault
18989
+ */
18990
+ async depositToVault(args) {
18991
+ const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
18992
+ const vaultPkg = this.getVaultPackageId();
18993
+ tx.moveCall({
18994
+ target: `${vaultPkg}::vault::deposit`,
18995
+ arguments: [
18996
+ tx.object(this.getDeploymentProtocolConfigId()),
18997
+ tx.object(this.getVaultConfigId()),
18998
+ tx.object("0x6"),
18999
+ tx.object(this.getBankId()),
19000
+ tx.object(this.getTxIndexerId()),
19001
+ tx.object(args.vaultID),
19002
+ nav,
19003
+ tx.pure.u128(formatNormalToWei(args.amount)),
19004
+ ],
19005
+ typeArguments: [this.getCurrencyType()],
19006
+ });
19007
+ return this.signAndExecuteVaultTx(tx);
19008
+ }
19009
+ /**
19010
+ * Request withdrawal from a vault (by share amount)
19011
+ */
19012
+ async requestWithdrawFromVault(args) {
19013
+ const tx = new transactions.Transaction();
19014
+ const vaultPkg = this.getVaultPackageId();
19015
+ tx.moveCall({
19016
+ target: `${vaultPkg}::vault::request_withdraw`,
19017
+ arguments: [
19018
+ tx.object(this.getDeploymentProtocolConfigId()),
19019
+ tx.object(this.getVaultConfigId()),
19020
+ tx.object("0x6"),
19021
+ tx.object(args.vaultID),
19022
+ tx.pure.u128(formatNormalToWei(args.shares)),
19023
+ ],
19024
+ typeArguments: [this.getCurrencyType()],
19025
+ });
19026
+ return this.signAndExecuteVaultTx(tx);
19027
+ }
19028
+ /**
19029
+ * Fill pending withdrawal requests (creator/operator only)
19030
+ */
19031
+ async fillWithdrawalRequests(args) {
19032
+ const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
19033
+ const vaultPkg = this.getVaultPackageId();
19034
+ const requestVec = tx.makeMoveVec({
19035
+ elements: args.withdrawalRequestIDs.map((id) => tx.object(id)),
19036
+ });
19037
+ tx.moveCall({
19038
+ target: `${vaultPkg}::vault::fill_withdrawal_requests`,
19039
+ arguments: [
19040
+ tx.object(this.getDeploymentProtocolConfigId()),
19041
+ tx.object(this.getVaultConfigId()),
19042
+ tx.object("0x6"),
19043
+ tx.object(this.getBankId()),
19044
+ tx.object(this.getTxIndexerId()),
19045
+ tx.object(args.vaultID),
19046
+ nav,
19047
+ requestVec,
19048
+ ],
19049
+ typeArguments: [this.getCurrencyType()],
19050
+ });
19051
+ return this.signAndExecuteVaultTx(tx);
19052
+ }
19053
+ /**
19054
+ * Close a vault (creator only)
19055
+ */
19056
+ async closeVault(args) {
19057
+ const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
19058
+ const vaultPkg = this.getVaultPackageId();
19059
+ tx.moveCall({
19060
+ target: `${vaultPkg}::vault::close_vault_v2`,
19061
+ arguments: [
19062
+ tx.object(this.getDeploymentProtocolConfigId()),
19063
+ tx.object(this.getVaultConfigId()),
19064
+ tx.object(this.getSubAccountsId()),
19065
+ tx.object(args.vaultID),
19066
+ tx.object(this.getBankId()),
19067
+ tx.object("0x6"),
19068
+ nav,
19069
+ ],
19070
+ typeArguments: [this.getCurrencyType()],
19071
+ });
19072
+ return this.signAndExecuteVaultTx(tx);
19073
+ }
19074
+ /**
19075
+ * Remove a closed vault (after all funds claimed)
19076
+ */
19077
+ async removeVault(args) {
19078
+ const tx = new transactions.Transaction();
19079
+ const vaultPkg = this.getVaultPackageId();
19080
+ tx.moveCall({
19081
+ target: `${vaultPkg}::vault::remove_vault`,
19082
+ arguments: [
19083
+ tx.object(this.getDeploymentProtocolConfigId()),
19084
+ tx.object(this.getVaultConfigId()),
19085
+ tx.object(this.getBankId()),
19086
+ tx.object(args.vaultID),
19087
+ ],
19088
+ typeArguments: [this.getCurrencyType()],
19089
+ });
19090
+ return this.signAndExecuteVaultTx(tx);
19091
+ }
19092
+ /**
19093
+ * Claim funds from a closed vault
19094
+ */
19095
+ async claimClosedVaultFunds(args) {
19096
+ const tx = new transactions.Transaction();
19097
+ const vaultPkg = this.getVaultPackageId();
19098
+ tx.moveCall({
19099
+ target: `${vaultPkg}::vault::claim_closed_vault_funds`,
19100
+ arguments: [
19101
+ tx.object(this.getDeploymentProtocolConfigId()),
19102
+ tx.object(this.getVaultConfigId()),
19103
+ tx.object(this.getBankId()),
19104
+ tx.object(this.getTxIndexerId()),
19105
+ tx.object(args.vaultID),
19106
+ ],
19107
+ typeArguments: [this.getCurrencyType()],
19108
+ });
19109
+ return this.signAndExecuteVaultTx(tx);
19110
+ }
19111
+ /**
19112
+ * Set the trader address for a vault (creator only)
19113
+ */
19114
+ async setVaultTrader(args) {
19115
+ const tx = new transactions.Transaction();
19116
+ const vaultPkg = this.getVaultPackageId();
19117
+ tx.moveCall({
19118
+ target: `${vaultPkg}::vault::set_trader`,
19119
+ arguments: [
19120
+ tx.object(this.getDeploymentProtocolConfigId()),
19121
+ tx.object(this.getVaultConfigId()),
19122
+ tx.object(args.vaultID),
19123
+ tx.object(this.getSubAccountsId()),
19124
+ tx.pure.address(args.newTrader),
19125
+ ],
19126
+ typeArguments: [this.getCurrencyType()],
19127
+ });
19128
+ return this.signAndExecuteVaultTx(tx);
19129
+ }
19130
+ /**
19131
+ * Add or remove a sub-trader for a vault
19132
+ */
19133
+ async setVaultSubTrader(args) {
19134
+ const tx = new transactions.Transaction();
19135
+ const vaultPkg = this.getVaultPackageId();
19136
+ tx.moveCall({
19137
+ target: `${vaultPkg}::vault::set_sub_trader`,
19138
+ arguments: [
19139
+ tx.object(this.getDeploymentProtocolConfigId()),
19140
+ tx.object(this.getVaultConfigId()),
19141
+ tx.object(args.vaultID),
19142
+ tx.object(this.getSubAccountsId()),
19143
+ tx.pure.address(args.subTrader),
19144
+ tx.pure.bool(args.status),
19145
+ ],
19146
+ typeArguments: [this.getCurrencyType()],
19147
+ });
19148
+ return this.signAndExecuteVaultTx(tx);
19149
+ }
19150
+ /**
19151
+ * Enable or disable deposits to a vault (creator only)
19152
+ */
19153
+ async setVaultDepositStatus(args) {
19154
+ const tx = new transactions.Transaction();
19155
+ const vaultPkg = this.getVaultPackageId();
19156
+ tx.moveCall({
19157
+ target: `${vaultPkg}::vault::set_deposit_status`,
19158
+ arguments: [
19159
+ tx.object(this.getDeploymentProtocolConfigId()),
19160
+ tx.object(this.getVaultConfigId()),
19161
+ tx.object(args.vaultID),
19162
+ tx.pure.bool(args.status),
19163
+ ],
19164
+ typeArguments: [this.getCurrencyType()],
19165
+ });
19166
+ return this.signAndExecuteVaultTx(tx);
19167
+ }
19168
+ /**
19169
+ * Set maximum USDC cap for a vault (creator only)
19170
+ */
19171
+ async setVaultMaxCap(args) {
19172
+ const tx = new transactions.Transaction();
19173
+ const vaultPkg = this.getVaultPackageId();
19174
+ tx.moveCall({
19175
+ target: `${vaultPkg}::vault::set_max_cap`,
19176
+ arguments: [
19177
+ tx.object(this.getDeploymentProtocolConfigId()),
19178
+ tx.object(this.getVaultConfigId()),
19179
+ tx.object(args.vaultID),
19180
+ tx.pure.u128(formatNormalToWei(args.maxCap)),
19181
+ ],
19182
+ typeArguments: [this.getCurrencyType()],
19183
+ });
19184
+ return this.signAndExecuteVaultTx(tx);
19185
+ }
19186
+ /**
19187
+ * Set minimum deposit amount for a vault (creator only)
19188
+ */
19189
+ async setVaultMinDepositAmount(args) {
19190
+ const tx = new transactions.Transaction();
19191
+ const vaultPkg = this.getVaultPackageId();
19192
+ tx.moveCall({
19193
+ target: `${vaultPkg}::vault::set_min_deposit_amount`,
19194
+ arguments: [
19195
+ tx.object(this.getDeploymentProtocolConfigId()),
19196
+ tx.object(this.getVaultConfigId()),
19197
+ tx.object(args.vaultID),
19198
+ tx.pure.u128(formatNormalToWei(args.minDepositAmount)),
19199
+ ],
19200
+ typeArguments: [this.getCurrencyType()],
19201
+ });
19202
+ return this.signAndExecuteVaultTx(tx);
19203
+ }
19204
+ /**
19205
+ * Enable or disable auto-close on withdrawal (creator only)
19206
+ */
19207
+ async setVaultAutoCloseOnWithdraw(args) {
19208
+ const tx = new transactions.Transaction();
19209
+ const vaultPkg = this.getVaultPackageId();
19210
+ tx.moveCall({
19211
+ target: `${vaultPkg}::vault::set_auto_close_on_withdraw`,
19212
+ arguments: [
19213
+ tx.object(this.getDeploymentProtocolConfigId()),
19214
+ tx.object(this.getVaultConfigId()),
19215
+ tx.object(args.vaultID),
19216
+ tx.pure.bool(args.autoCloseOnWithdraw),
19217
+ ],
19218
+ typeArguments: [this.getCurrencyType()],
19219
+ });
19220
+ return this.signAndExecuteVaultTx(tx);
19221
+ }
19222
+ /**
19223
+ * Read vault object data from chain
19224
+ */
19225
+ async getVaultInfo(vaultID) {
19226
+ try {
19227
+ const obj = await this.suiClient.getObject({
19228
+ id: vaultID,
19229
+ options: { showContent: true, showType: true },
19230
+ });
19231
+ if (!obj.data?.content || obj.data.content.dataType !== "moveObject") {
19232
+ return { status: false, error: "Vault object not found or not a Move object" };
19233
+ }
19234
+ return { status: true, data: obj.data.content.fields };
19235
+ }
19236
+ catch (error) {
19237
+ return { status: false, error: formatError(error) };
19238
+ }
19239
+ }
19240
+ /**
19241
+ * List vaults created by a specific address via server API.
19242
+ */
19243
+ async listVaults(creatorAddress) {
19244
+ try {
19245
+ const address = creatorAddress || this.walletAddress;
19246
+ const response = await this.httpClient.get(API_ENDPOINTS.VAULTS_BY_CREATOR, {
19247
+ params: { address },
19248
+ });
19249
+ if (response.code === 200) {
19250
+ return { status: true, data: response.data || [] };
19251
+ }
19252
+ return { status: false, error: response.message || "Failed to list vaults" };
19253
+ }
19254
+ catch (error) {
19255
+ return { status: false, error: formatError(error) };
19256
+ }
19257
+ }
18498
19258
  }
18499
19259
 
18500
19260
  // Copyright (c) 2025 Dipcoin LLC
@@ -18509,33 +19269,133 @@ function initDipCoinPerpSDK(privateKey, options) {
18509
19269
  return new DipCoinPerpSDK(privateKey, sdkOptions);
18510
19270
  }
18511
19271
 
18512
- dotenv__default["default"].config();
18513
- let sdkInstance = null;
18514
- function getSDK() {
18515
- if (sdkInstance)
18516
- return sdkInstance;
18517
- const privateKey = process.env.PRIVATE_KEY;
18518
- if (!privateKey) {
18519
- console.error("Error: PRIVATE_KEY not set in .env");
19272
+ const globalEnv = path__default["default"].join(os__default["default"].homedir(), ".config", "dipcoin", "env");
19273
+ const localEnv = path__default["default"].resolve(process.cwd(), ".env");
19274
+ if (fs__default["default"].existsSync(globalEnv)) {
19275
+ dotenv__default["default"].config({ path: globalEnv });
19276
+ }
19277
+ else if (fs__default["default"].existsSync(localEnv)) {
19278
+ dotenv__default["default"].config({ path: localEnv });
19279
+ }
19280
+ // Cache SDK instances by a composite key
19281
+ const sdkCache = new Map();
19282
+ /**
19283
+ * Derive an Ed25519 keypair from a mnemonic at a given account index.
19284
+ * Uses SLIP-0010 path: m/44'/784'/{index}'/0'/0'
19285
+ */
19286
+ function deriveKeypairFromMnemonic(mnemonic, index) {
19287
+ const path = `m/44'/784'/${index}'/0'/0'`;
19288
+ return ed25519.Ed25519Keypair.deriveKeypair(mnemonic, path);
19289
+ }
19290
+ /**
19291
+ * Read and validate MNEMONIC from env.
19292
+ */
19293
+ function getMnemonic() {
19294
+ const mnemonic = process.env.MNEMONIC;
19295
+ if (!mnemonic) {
19296
+ console.error(`Error: MNEMONIC not set. Create config at ${globalEnv}`);
18520
19297
  process.exit(1);
18521
19298
  }
18522
- const network = process.env.NETWORK || "testnet";
18523
- const subAccountKey = process.env.SUB_ACCOUNT_KEY || undefined;
18524
- sdkInstance = initDipCoinPerpSDK(privateKey, {
18525
- network,
18526
- ...(subAccountKey ? { subAccountKey } : {}),
18527
- });
18528
- return sdkInstance;
19299
+ return mnemonic;
18529
19300
  }
18530
- function getVaultAddress(cmdVault) {
18531
- return cmdVault || process.env.VAULT_ADDRESS || undefined;
19301
+ /**
19302
+ * Get the network from env.
19303
+ */
19304
+ function getNetwork() {
19305
+ return process.env.NETWORK || "testnet";
18532
19306
  }
18533
- async function ensureAuth(sdk) {
18534
- const auth = await sdk.authenticate();
18535
- if (!auth.status) {
18536
- console.error("Authentication failed:", auth.error);
18537
- process.exit(1);
19307
+ /**
19308
+ * Trading mode SDK: main keypair = index 0, sub keypair = index N.
19309
+ * Used for order signing where creator = main address, signer = sub keypair.
19310
+ * When no vaultIndex provided, returns SDK with main account only (index 0).
19311
+ */
19312
+ function getSDK(vaultIndex) {
19313
+ const cacheKey = `trading:${vaultIndex ?? "main"}`;
19314
+ if (sdkCache.has(cacheKey))
19315
+ return sdkCache.get(cacheKey);
19316
+ const mnemonic = getMnemonic();
19317
+ const network = getNetwork();
19318
+ const mainKeypair = deriveKeypairFromMnemonic(mnemonic, 0);
19319
+ if (vaultIndex !== undefined && vaultIndex > 0) {
19320
+ const subKeypair = deriveKeypairFromMnemonic(mnemonic, vaultIndex);
19321
+ const sdk = initDipCoinPerpSDK(mainKeypair, { network, subAccountKey: subKeypair });
19322
+ sdkCache.set(cacheKey, sdk);
19323
+ return sdk;
19324
+ }
19325
+ const sdk = initDipCoinPerpSDK(mainKeypair, { network });
19326
+ sdkCache.set(cacheKey, sdk);
19327
+ return sdk;
19328
+ }
19329
+ /**
19330
+ * Vault on-chain mode SDK: main keypair = index N.
19331
+ * Used for vault's own deposit/withdraw/balance where the vault keypair signs the tx.
19332
+ */
19333
+ function getVaultSDK(vaultIndex) {
19334
+ const cacheKey = `vault:${vaultIndex}`;
19335
+ if (sdkCache.has(cacheKey))
19336
+ return sdkCache.get(cacheKey);
19337
+ const mnemonic = getMnemonic();
19338
+ const network = getNetwork();
19339
+ const vaultKeypair = deriveKeypairFromMnemonic(mnemonic, vaultIndex);
19340
+ const sdk = initDipCoinPerpSDK(vaultKeypair, { network });
19341
+ sdkCache.set(cacheKey, sdk);
19342
+ return sdk;
19343
+ }
19344
+ /**
19345
+ * Resolve vault address from vault index or explicit address.
19346
+ * Returns undefined for main account (no vault).
19347
+ */
19348
+ function resolveVaultAddress(vaultIndex, vaultAddr) {
19349
+ if (vaultAddr)
19350
+ return vaultAddr;
19351
+ if (vaultIndex !== undefined && vaultIndex > 0) {
19352
+ const keypair = deriveKeypairFromMnemonic(getMnemonic(), vaultIndex);
19353
+ return keypair.getPublicKey().toSuiAddress();
19354
+ }
19355
+ return undefined;
19356
+ }
19357
+ /**
19358
+ * List derived vault addresses from mnemonic.
19359
+ */
19360
+ function listVaultAddresses(count) {
19361
+ const mnemonic = getMnemonic();
19362
+ const results = [];
19363
+ for (let i = 0; i < count; i++) {
19364
+ const path = `m/44'/784'/${i}'/0'/0'`;
19365
+ const keypair = deriveKeypairFromMnemonic(mnemonic, i);
19366
+ results.push({ index: i, address: keypair.getPublicKey().toSuiAddress(), path });
19367
+ }
19368
+ return results;
19369
+ }
19370
+ /**
19371
+ * Get the appropriate SDK for on-chain operations (deposit/withdraw/margin).
19372
+ * Uses the vault's own keypair when vault-index > 0, otherwise main account.
19373
+ */
19374
+ function getOnChainSDK(vaultIndex) {
19375
+ return (vaultIndex !== undefined && vaultIndex > 0) ? getVaultSDK(vaultIndex) : getSDK();
19376
+ }
19377
+
19378
+ /**
19379
+ * Get the global --vault-index value from the root program.
19380
+ */
19381
+ function getGlobalVaultIndex(prog) {
19382
+ const opts = prog.opts();
19383
+ if (opts.vaultIndex !== undefined) {
19384
+ const n = Number(opts.vaultIndex);
19385
+ if (isNaN(n) || n < 0) {
19386
+ console.error("Error: --vault-index must be a non-negative integer");
19387
+ process.exit(1);
19388
+ }
19389
+ return n;
18538
19390
  }
19391
+ // Fallback to DEFAULT_VAULT_INDEX env
19392
+ const envDefault = process.env.DEFAULT_VAULT_INDEX;
19393
+ if (envDefault !== undefined) {
19394
+ const n = Number(envDefault);
19395
+ if (!isNaN(n) && n >= 0)
19396
+ return n;
19397
+ }
19398
+ return undefined;
18539
19399
  }
18540
19400
 
18541
19401
  function isJson(cmd) {
@@ -18561,6 +19421,26 @@ function handleError(error) {
18561
19421
  console.error("Error:", typeof error === "string" ? error : error?.message || JSON.stringify(error));
18562
19422
  process.exit(1);
18563
19423
  }
19424
+ /**
19425
+ * Print a standard transaction result (success message or failure with exit).
19426
+ */
19427
+ function printTxResult(program, tx, successMsg) {
19428
+ const status = tx?.effects?.status?.status;
19429
+ const error = tx?.effects?.status?.error;
19430
+ if (isJson(program))
19431
+ return printJson({ digest: tx?.digest, status, error });
19432
+ if (status === "failure") {
19433
+ console.error(`Failed: ${error}`);
19434
+ process.exit(1);
19435
+ }
19436
+ console.log(`${successMsg} Tx: ${tx?.digest}`);
19437
+ }
19438
+ /**
19439
+ * Normalize a symbol to include "-PERP" suffix if missing.
19440
+ */
19441
+ function normalizeSymbol(s) {
19442
+ return s.includes("-") ? s : `${s}-PERP`;
19443
+ }
18564
19444
 
18565
19445
  function registerAccountCommands(program) {
18566
19446
  const account = program.command("account").description("Account operations");
@@ -18570,17 +19450,18 @@ function registerAccountCommands(program) {
18570
19450
  .option("--vault <address>", "Vault address")
18571
19451
  .action(async (opts) => {
18572
19452
  try {
18573
- const sdk = getSDK();
18574
- await ensureAuth(sdk);
18575
- const parentAddress = opts.vault || sdk.address;
19453
+ const vaultIndex = getGlobalVaultIndex(program);
19454
+ const sdk = getSDK(vaultIndex);
19455
+ const parentAddress = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
18576
19456
  const result = await sdk.getAccountInfo({ parentAddress });
18577
19457
  if (!result.status || !result.data)
18578
19458
  return handleError(result.error);
18579
19459
  if (isJson(program))
18580
19460
  return printJson(result.data);
18581
19461
  const d = result.data;
19462
+ const label = parentAddress !== sdk.address ? parentAddress : sdk.address;
18582
19463
  printTable(["Field", "Value"], [
18583
- ["Wallet", sdk.address],
19464
+ ["Wallet", label],
18584
19465
  ["Wallet Balance", formatWei(d.walletBalance)],
18585
19466
  ["Account Value", formatWei(d.accountValue)],
18586
19467
  ["Free Collateral", formatWei(d.freeCollateral)],
@@ -18598,18 +19479,10 @@ function registerAccountCommands(program) {
18598
19479
  .argument("<amount>", "Amount in USDC")
18599
19480
  .action(async (amount) => {
18600
19481
  try {
18601
- const sdk = getSDK();
19482
+ const vaultIndex = getGlobalVaultIndex(program);
19483
+ const sdk = getOnChainSDK(vaultIndex);
18602
19484
  const tx = await sdk.depositToBank(Number(amount));
18603
- const status = tx?.effects?.status?.status;
18604
- const error = tx?.effects?.status?.error;
18605
- if (isJson(program))
18606
- return printJson({ digest: tx?.digest, status, error });
18607
- if (status === "failure") {
18608
- console.error(`Deposit ${amount} USDC failed. Tx: ${tx?.digest}`);
18609
- console.error(`Error: ${error}`);
18610
- process.exit(1);
18611
- }
18612
- console.log(`Deposit ${amount} USDC succeeded. Tx: ${tx?.digest}`);
19485
+ printTxResult(program, tx, `Deposit ${amount} USDC succeeded.`);
18613
19486
  }
18614
19487
  catch (e) {
18615
19488
  handleError(e);
@@ -18621,18 +19494,10 @@ function registerAccountCommands(program) {
18621
19494
  .argument("<amount>", "Amount in USDC")
18622
19495
  .action(async (amount) => {
18623
19496
  try {
18624
- const sdk = getSDK();
19497
+ const vaultIndex = getGlobalVaultIndex(program);
19498
+ const sdk = getOnChainSDK(vaultIndex);
18625
19499
  const tx = await sdk.withdrawFromBank(Number(amount));
18626
- const status = tx?.effects?.status?.status;
18627
- const error = tx?.effects?.status?.error;
18628
- if (isJson(program))
18629
- return printJson({ digest: tx?.digest, status, error });
18630
- if (status === "failure") {
18631
- console.error(`Withdraw ${amount} USDC failed. Tx: ${tx?.digest}`);
18632
- console.error(`Error: ${error}`);
18633
- process.exit(1);
18634
- }
18635
- console.log(`Withdraw ${amount} USDC succeeded. Tx: ${tx?.digest}`);
19500
+ printTxResult(program, tx, `Withdraw ${amount} USDC succeeded.`);
18636
19501
  }
18637
19502
  catch (e) {
18638
19503
  handleError(e);
@@ -18640,18 +19505,41 @@ function registerAccountCommands(program) {
18640
19505
  });
18641
19506
  }
18642
19507
 
18643
- async function placeOrder(program, side, symbol, quantity, opts) {
19508
+ /**
19509
+ * Parse amount string: "100USDC" -> 100, "100" -> 100
19510
+ */
19511
+ function parseUsdcAmount(raw) {
19512
+ const cleaned = raw.replace(/usdc$/i, "");
19513
+ const val = parseFloat(cleaned);
19514
+ if (isNaN(val) || val <= 0)
19515
+ throw new Error(`Invalid amount: ${raw}`);
19516
+ return val;
19517
+ }
19518
+ /**
19519
+ * Parse leverage string: "10x" -> "10", "10" -> "10"
19520
+ */
19521
+ function parseLeverage(raw) {
19522
+ const cleaned = raw.replace(/x$/i, "");
19523
+ const val = parseFloat(cleaned);
19524
+ if (isNaN(val) || val <= 0)
19525
+ throw new Error(`Invalid leverage: ${raw}`);
19526
+ return cleaned;
19527
+ }
19528
+ async function placeOrder(program, side, symbol, amount, leverage, opts) {
18644
19529
  try {
18645
- const sdk = getSDK();
18646
- await ensureAuth(sdk);
18647
- const leverage = opts.leverage || "10";
18648
- const orderType = opts.type?.toUpperCase() === "LIMIT" ? OrderType.LIMIT : OrderType.MARKET;
18649
- const vault = opts.vault || undefined;
18650
- if (quantity && opts.usdc)
18651
- return handleError("Cannot specify both <quantity> and --usdc");
18652
- if (!quantity && !opts.usdc)
18653
- return handleError("Must specify either <quantity> or --usdc <amount>");
18654
- if (opts.usdc) {
19530
+ const vaultIndex = getGlobalVaultIndex(program);
19531
+ const sdk = getSDK(vaultIndex);
19532
+ const lev = parseLeverage(leverage);
19533
+ const orderType = opts.price ? OrderType.LIMIT : OrderType.MARKET;
19534
+ symbol = normalizeSymbol(symbol);
19535
+ let quantity;
19536
+ if (opts.qty) {
19537
+ // Explicit quantity mode
19538
+ quantity = opts.qty;
19539
+ }
19540
+ else {
19541
+ // Default: USDC margin mode
19542
+ const usdcAmount = parseUsdcAmount(amount);
18655
19543
  const tickerResult = await sdk.getTicker(symbol);
18656
19544
  if (!tickerResult.status || !tickerResult.data)
18657
19545
  return handleError("Failed to get market price for USDC conversion");
@@ -18659,9 +19547,7 @@ async function placeOrder(program, side, symbol, quantity, opts) {
18659
19547
  if (!priceWei || priceWei <= 0)
18660
19548
  return handleError("Invalid market price");
18661
19549
  const price = priceWei / 1e18;
18662
- const lev = parseFloat(leverage);
18663
- const usdcAmount = parseFloat(opts.usdc);
18664
- const rawQty = usdcAmount * lev / price;
19550
+ const rawQty = usdcAmount * parseFloat(lev) / price;
18665
19551
  let stepSize = 0.01;
18666
19552
  const pairResult = await sdk.getTradingPairs();
18667
19553
  if (pairResult.status && pairResult.data) {
@@ -18682,7 +19568,7 @@ async function placeOrder(program, side, symbol, quantity, opts) {
18682
19568
  quantity = stepped.toFixed(decimals);
18683
19569
  if (parseFloat(quantity) <= 0)
18684
19570
  return handleError(`USDC amount too small, minimum quantity step is ${stepSize}`);
18685
- console.log(`USDC ${opts.usdc} -> quantity ${quantity} (price: ${price}, leverage: ${lev}x, step: ${stepSize})`);
19571
+ console.log(`USDC ${usdcAmount} -> quantity ${quantity} (price: ${price}, leverage: ${lev}x, step: ${stepSize})`);
18686
19572
  }
18687
19573
  const perpId = await sdk.getPerpetualID(symbol);
18688
19574
  if (!perpId)
@@ -18693,16 +19579,22 @@ async function placeOrder(program, side, symbol, quantity, opts) {
18693
19579
  side,
18694
19580
  orderType,
18695
19581
  quantity,
18696
- leverage,
19582
+ leverage: lev,
18697
19583
  reduceOnly: opts.reduceOnly || false,
18698
19584
  };
18699
- if (orderType === OrderType.LIMIT) {
18700
- if (!opts.price)
18701
- return handleError("--price is required for LIMIT orders");
19585
+ if (opts.price) {
18702
19586
  params.price = opts.price;
18703
19587
  }
18704
- if (vault)
18705
- params.creator = vault;
19588
+ // --vault <address> as explicit creator override (fallback)
19589
+ if (opts.vault) {
19590
+ params.creator = opts.vault;
19591
+ }
19592
+ else {
19593
+ // With vault-index, the SDK handles creator via sub-keypair automatically
19594
+ const vaultAddr = resolveVaultAddress(vaultIndex);
19595
+ if (vaultAddr)
19596
+ params.creator = vaultAddr;
19597
+ }
18706
19598
  if (opts.tp)
18707
19599
  params.tpTriggerPrice = opts.tp;
18708
19600
  if (opts.sl)
@@ -18723,24 +19615,24 @@ async function placeOrder(program, side, symbol, quantity, opts) {
18723
19615
  function registerTradeCommands(program) {
18724
19616
  const trade = program.command("trade").description("Trading operations");
18725
19617
  const orderOpts = (cmd) => cmd
18726
- .option("--leverage <n>", "Leverage multiplier", "10")
18727
- .option("--price <p>", "Order price (required for limit)")
18728
- .option("--type <type>", "Order type: market or limit", "market")
19618
+ .option("--qty <quantity>", "Specify order quantity instead of USDC margin")
19619
+ .option("--price <p>", "Limit order price (auto-enables limit order)")
18729
19620
  .option("--reduce-only", "Reduce only order")
18730
19621
  .option("--tp <price>", "Take profit trigger price")
18731
19622
  .option("--sl <price>", "Stop loss trigger price")
18732
- .option("--vault <address>", "Vault/creator address")
18733
- .option("--usdc <amount>", "Margin amount in USDC (alternative to quantity)");
19623
+ .option("--vault <address>", "Vault/creator address (fallback)");
18734
19624
  orderOpts(trade
18735
19625
  .command("buy")
18736
- .description("Place a BUY order")
18737
- .argument("<symbol>", "Trading pair (e.g. BTC-PERP)")
18738
- .argument("[quantity]", "Order quantity (or use --usdc)")).action((symbol, quantity, opts) => placeOrder(program, OrderSide.BUY, symbol, quantity, opts));
19626
+ .description("Place a BUY order (e.g. trade buy BTC 100USDC 10x)")
19627
+ .argument("<symbol>", "Trading pair (e.g. BTC or BTC-PERP)")
19628
+ .argument("<amount>", "USDC margin amount (e.g. 100 or 100USDC)")
19629
+ .argument("<leverage>", "Leverage multiplier (e.g. 10 or 10x)")).action((symbol, amount, leverage, opts) => placeOrder(program, OrderSide.BUY, symbol, amount, leverage, opts));
18739
19630
  orderOpts(trade
18740
19631
  .command("sell")
18741
- .description("Place a SELL order")
18742
- .argument("<symbol>", "Trading pair (e.g. BTC-PERP)")
18743
- .argument("[quantity]", "Order quantity (or use --usdc)")).action((symbol, quantity, opts) => placeOrder(program, OrderSide.SELL, symbol, quantity, opts));
19632
+ .description("Place a SELL order (e.g. trade sell BTC 100USDC 10x)")
19633
+ .argument("<symbol>", "Trading pair (e.g. BTC or BTC-PERP)")
19634
+ .argument("<amount>", "USDC margin amount (e.g. 100 or 100USDC)")
19635
+ .argument("<leverage>", "Leverage multiplier (e.g. 10 or 10x)")).action((symbol, amount, leverage, opts) => placeOrder(program, OrderSide.SELL, symbol, amount, leverage, opts));
18744
19636
  trade
18745
19637
  .command("cancel")
18746
19638
  .description("Cancel orders by hash")
@@ -18749,13 +19641,14 @@ function registerTradeCommands(program) {
18749
19641
  .option("--vault <address>", "Parent address")
18750
19642
  .action(async (symbol, hashes, opts) => {
18751
19643
  try {
18752
- const sdk = getSDK();
18753
- await ensureAuth(sdk);
18754
- const vault = opts.vault || sdk.address;
19644
+ symbol = normalizeSymbol(symbol);
19645
+ const vaultIndex = getGlobalVaultIndex(program);
19646
+ const sdk = getSDK(vaultIndex);
19647
+ const parentAddress = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
18755
19648
  const result = await sdk.cancelOrder({
18756
19649
  symbol,
18757
19650
  orderHashes: hashes,
18758
- parentAddress: vault,
19651
+ parentAddress,
18759
19652
  });
18760
19653
  if (!result.status)
18761
19654
  return handleError(result.error);
@@ -18778,9 +19671,9 @@ function registerPositionCommands(program) {
18778
19671
  .option("--vault <address>", "Vault address")
18779
19672
  .action(async (opts) => {
18780
19673
  try {
18781
- const sdk = getSDK();
18782
- await ensureAuth(sdk);
18783
- const vault = opts.vault || sdk.address;
19674
+ const vaultIndex = getGlobalVaultIndex(program);
19675
+ const sdk = getSDK(vaultIndex);
19676
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
18784
19677
  const params = {
18785
19678
  parentAddress: vault,
18786
19679
  ...(opts.symbol ? { symbol: opts.symbol } : {}),
@@ -18823,7 +19716,7 @@ function registerPositionCommands(program) {
18823
19716
  .action(async (symbol, opts) => {
18824
19717
  try {
18825
19718
  const sdk = getSDK();
18826
- await ensureAuth(sdk);
19719
+ symbol = normalizeSymbol(symbol);
18827
19720
  const perpId = await sdk.getPerpetualID(symbol);
18828
19721
  if (!perpId)
18829
19722
  return handleError(`PerpetualID not found for ${symbol}`);
@@ -18872,7 +19765,8 @@ function registerPositionCommands(program) {
18872
19765
  .argument("<amount>", "Amount in USDC")
18873
19766
  .action(async (symbol, amount) => {
18874
19767
  try {
18875
- const sdk = getSDK();
19768
+ const vaultIndex = getGlobalVaultIndex(program);
19769
+ const sdk = getOnChainSDK(vaultIndex);
18876
19770
  const tx = await sdk.addMargin({ symbol, amount: Number(amount) });
18877
19771
  if (isJson(program))
18878
19772
  return printJson({ digest: tx?.digest, status: "ok" });
@@ -18889,7 +19783,8 @@ function registerPositionCommands(program) {
18889
19783
  .argument("<amount>", "Amount in USDC")
18890
19784
  .action(async (symbol, amount) => {
18891
19785
  try {
18892
- const sdk = getSDK();
19786
+ const vaultIndex = getGlobalVaultIndex(program);
19787
+ const sdk = getOnChainSDK(vaultIndex);
18893
19788
  const tx = await sdk.removeMargin({ symbol, amount: Number(amount) });
18894
19789
  if (isJson(program))
18895
19790
  return printJson({ digest: tx?.digest, status: "ok" });
@@ -18926,6 +19821,7 @@ function registerMarketCommands(program) {
18926
19821
  .argument("<symbol>", "Trading pair")
18927
19822
  .action(async (symbol) => {
18928
19823
  try {
19824
+ symbol = normalizeSymbol(symbol);
18929
19825
  const sdk = getSDK();
18930
19826
  const result = await sdk.getTicker(symbol);
18931
19827
  if (!result.status || !result.data)
@@ -18954,6 +19850,7 @@ function registerMarketCommands(program) {
18954
19850
  .argument("<symbol>", "Trading pair")
18955
19851
  .action(async (symbol) => {
18956
19852
  try {
19853
+ symbol = normalizeSymbol(symbol);
18957
19854
  const sdk = getSDK();
18958
19855
  const result = await sdk.getOrderBook(symbol);
18959
19856
  if (!result.status || !result.data)
@@ -18981,6 +19878,7 @@ function registerMarketCommands(program) {
18981
19878
  .argument("<symbol>", "Trading pair")
18982
19879
  .action(async (symbol) => {
18983
19880
  try {
19881
+ symbol = normalizeSymbol(symbol);
18984
19882
  const sdk = getSDK();
18985
19883
  const result = await sdk.getOraclePrice(symbol);
18986
19884
  if (!result.status)
@@ -19004,16 +19902,20 @@ function registerHistoryCommands(program) {
19004
19902
  .option("--page <n>", "Page number", "1")
19005
19903
  .option("--size <n>", "Page size", "20")
19006
19904
  .option("--vault <address>", "Vault address")
19905
+ .option("--begin-time <ms>", "Begin time filter (epoch ms)")
19906
+ .option("--end-time <ms>", "End time filter (epoch ms)")
19007
19907
  .action(async (opts) => {
19008
19908
  try {
19009
- const sdk = getSDK();
19010
- await ensureAuth(sdk);
19011
- const vault = opts.vault || sdk.address;
19909
+ const vaultIndex = getGlobalVaultIndex(program);
19910
+ const sdk = getSDK(vaultIndex);
19911
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
19012
19912
  const result = await sdk.getHistoryOrders({
19013
19913
  ...(opts.symbol ? { symbol: opts.symbol } : {}),
19014
19914
  page: Number(opts.page),
19015
19915
  pageSize: Number(opts.size),
19016
19916
  parentAddress: vault,
19917
+ ...(opts.beginTime ? { beginTime: Number(opts.beginTime) } : {}),
19918
+ ...(opts.endTime ? { endTime: Number(opts.endTime) } : {}),
19017
19919
  });
19018
19920
  if (!result.status)
19019
19921
  return handleError(result.error);
@@ -19043,16 +19945,18 @@ function registerHistoryCommands(program) {
19043
19945
  .option("--page <n>", "Page number", "1")
19044
19946
  .option("--size <n>", "Page size", "20")
19045
19947
  .option("--vault <address>", "Vault address")
19948
+ .option("--begin-time <ms>", "Begin time filter (epoch ms)")
19046
19949
  .action(async (opts) => {
19047
19950
  try {
19048
- const sdk = getSDK();
19049
- await ensureAuth(sdk);
19050
- const vault = opts.vault || sdk.address;
19951
+ const vaultIndex = getGlobalVaultIndex(program);
19952
+ const sdk = getSDK(vaultIndex);
19953
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
19051
19954
  const result = await sdk.getFundingSettlements({
19052
19955
  ...(opts.symbol ? { symbol: opts.symbol } : {}),
19053
19956
  page: Number(opts.page),
19054
19957
  pageSize: Number(opts.size),
19055
19958
  parentAddress: vault,
19959
+ ...(opts.beginTime ? { beginTime: Number(opts.beginTime) } : {}),
19056
19960
  });
19057
19961
  if (!result.status)
19058
19962
  return handleError(result.error);
@@ -19060,12 +19964,13 @@ function registerHistoryCommands(program) {
19060
19964
  return printJson(result.data);
19061
19965
  if (!result.data?.data?.length)
19062
19966
  return console.log("No funding settlements.");
19063
- printTable(["Symbol", "Rate", "Fee", "Qty", "Side", "Time"], result.data.data.map((f) => [
19967
+ printTable(["Symbol", "Side", "Size", "Rate", "Settlement", "Price", "Time"], result.data.data.map((f) => [
19064
19968
  f.symbol || "-",
19969
+ f.positionIsLong === 1 ? "LONG" : f.positionIsLong === 0 ? "SHORT" : (f.side || "-"),
19970
+ formatWei(f.size || f.quantity),
19065
19971
  formatWei(f.fundingRate),
19066
- formatWei(f.fundingFee),
19067
- formatWei(f.quantity),
19068
- f.side || "-",
19972
+ formatWei(f.settlementAmount || f.fundingFee),
19973
+ formatWei(f.oraclePrice),
19069
19974
  f.createdAt ? new Date(f.createdAt).toLocaleString() : "-",
19070
19975
  ]));
19071
19976
  }
@@ -19079,15 +19984,17 @@ function registerHistoryCommands(program) {
19079
19984
  .option("--page <n>", "Page number", "1")
19080
19985
  .option("--size <n>", "Page size", "20")
19081
19986
  .option("--vault <address>", "Vault address")
19987
+ .option("--begin-time <ms>", "Begin time filter (epoch ms)")
19082
19988
  .action(async (opts) => {
19083
19989
  try {
19084
- const sdk = getSDK();
19085
- await ensureAuth(sdk);
19086
- const vault = opts.vault || sdk.address;
19990
+ const vaultIndex = getGlobalVaultIndex(program);
19991
+ const sdk = getSDK(vaultIndex);
19992
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
19087
19993
  const result = await sdk.getBalanceChanges({
19088
19994
  page: Number(opts.page),
19089
19995
  pageSize: Number(opts.size),
19090
19996
  parentAddress: vault,
19997
+ ...(opts.beginTime ? { beginTime: Number(opts.beginTime) } : {}),
19091
19998
  });
19092
19999
  if (!result.status)
19093
20000
  return handleError(result.error);
@@ -19095,12 +20002,11 @@ function registerHistoryCommands(program) {
19095
20002
  return printJson(result.data);
19096
20003
  if (!result.data?.data?.length)
19097
20004
  return console.log("No balance changes.");
19098
- printTable(["Type", "Amount", "Balance", "Symbol", "Time"], result.data.data.map((b) => [
19099
- b.type || "-",
19100
- formatWei(b.amount),
19101
- formatWei(b.balance),
19102
- b.symbol || "-",
19103
- b.createdAt ? new Date(b.createdAt).toLocaleString() : "-",
20005
+ printTable(["Type", "Amount", "TxDigest", "Time"], result.data.data.map((b) => [
20006
+ b.bizTypeDesc || b.type || "-",
20007
+ formatWei(b.settlementAmount || b.amount),
20008
+ b.txDigest ? b.txDigest.slice(0, 16) + "..." : "-",
20009
+ (b.createdTime || b.createdAt) ? new Date(b.createdTime || b.createdAt).toLocaleString() : "-",
19104
20010
  ]));
19105
20011
  }
19106
20012
  catch (e) {
@@ -19109,55 +20015,338 @@ function registerHistoryCommands(program) {
19109
20015
  });
19110
20016
  }
19111
20017
 
20018
+ const USDC_DECIMALS = 6;
20019
+ /**
20020
+ * Format a USDC atomic value (6 decimals) to human-readable.
20021
+ */
20022
+ function formatUsdc(value) {
20023
+ if (value === undefined || value === null || value === "")
20024
+ return "0";
20025
+ const bn = new BigNumber__default["default"](String(value));
20026
+ if (bn.isNaN())
20027
+ return "0";
20028
+ return bn.dividedBy(new BigNumber__default["default"](10).pow(USDC_DECIMALS)).toFixed(USDC_DECIMALS);
20029
+ }
20030
+ /**
20031
+ * Format a ratio (9-decimal, where 10^9 = 100%) to percentage string.
20032
+ */
20033
+ function formatRatio(value) {
20034
+ if (value === undefined || value === null || value === "")
20035
+ return "0%";
20036
+ const bn = new BigNumber__default["default"](String(value));
20037
+ if (bn.isNaN())
20038
+ return "0%";
20039
+ return bn.dividedBy(new BigNumber__default["default"](10).pow(7)).toFixed(2) + "%";
20040
+ }
20041
+ /**
20042
+ * Convert percentage to 9-decimal ratio string.
20043
+ * E.g., "10" (10%) -> "100000000" (10 * 10^7)
20044
+ */
20045
+ function percentToRatio(pct) {
20046
+ const val = parseFloat(pct);
20047
+ if (isNaN(val) || val < 0)
20048
+ throw new Error(`Invalid percentage: ${pct}`);
20049
+ return new BigNumber__default["default"](val).multipliedBy(new BigNumber__default["default"](10).pow(7)).toFixed(0);
20050
+ }
19112
20051
  function registerVaultCommands(program) {
19113
- const vault = program.command("vault").description("Vault / sub-account operations");
20052
+ const vault = program.command("vault").description("On-chain vault operations");
20053
+ // === vault create ===
19114
20054
  vault
19115
- .command("set-sub-account")
19116
- .description("Set sub-account on-chain (authorize a sub-account address)")
19117
- .argument("<subAddress>", "Sub-account address to authorize")
19118
- .action(async (subAddress) => {
20055
+ .command("create")
20056
+ .description("Create a new on-chain vault")
20057
+ .requiredOption("--name <name>", "Vault display name")
20058
+ .requiredOption("--trader <address>", "Trader address")
20059
+ .requiredOption("--max-cap <usdc>", "Maximum USDC cap")
20060
+ .requiredOption("--min-deposit <usdc>", "Minimum deposit amount in USDC")
20061
+ .requiredOption("--creator-share <pct>", "Creator minimum share ratio (%)")
20062
+ .requiredOption("--profit-share <pct>", "Creator profit share ratio (%)")
20063
+ .requiredOption("--initial <usdc>", "Initial deposit in USDC")
20064
+ .action(async (opts) => {
19119
20065
  try {
19120
20066
  const sdk = getSDK();
19121
- const tx = await sdk.setSubAccount(subAddress);
20067
+ console.log(`Creating vault "${opts.name}"...`);
20068
+ const tx = await sdk.createVault({
20069
+ name: opts.name,
20070
+ trader: opts.trader,
20071
+ maxCap: Number(opts.maxCap),
20072
+ minDepositAmount: Number(opts.minDeposit),
20073
+ creatorMinimumShareRatio: percentToRatio(opts.creatorShare),
20074
+ creatorProfitShareRatio: percentToRatio(opts.profitShare),
20075
+ initialAmount: Number(opts.initial),
20076
+ });
20077
+ printTxResult(program, tx, `Vault "${opts.name}" created.`);
20078
+ }
20079
+ catch (e) {
20080
+ handleError(e);
20081
+ }
20082
+ });
20083
+ // === vault list ===
20084
+ vault
20085
+ .command("list")
20086
+ .description("List vaults created by current wallet")
20087
+ .action(async () => {
20088
+ try {
20089
+ const sdk = getSDK();
20090
+ const result = await sdk.listVaults();
20091
+ if (!result.status)
20092
+ return handleError(result.error);
19122
20093
  if (isJson(program))
19123
- return printJson({ digest: tx?.digest, status: "ok" });
19124
- console.log(`Sub-account ${subAddress} set. Tx: ${tx?.digest || JSON.stringify(tx)}`);
20094
+ return printJson(result.data);
20095
+ if (!result.data?.length)
20096
+ return console.log("No vaults found.");
20097
+ printTable(["Vault ID", "Name", "Trader", "Deposit", "Status"], result.data.map((v) => [
20098
+ v.vaultId,
20099
+ v.name || "-",
20100
+ v.trader,
20101
+ v.depositStatus === 1 ? "Open" : "Closed",
20102
+ v.closedAt > 0 ? "Closed" : "Active",
20103
+ ]));
19125
20104
  }
19126
20105
  catch (e) {
19127
20106
  handleError(e);
19128
20107
  }
19129
20108
  });
20109
+ // === vault info ===
19130
20110
  vault
19131
20111
  .command("info")
19132
- .description("Show vault account info")
19133
- .option("--address <address>", "Vault address (or use VAULT_ADDRESS env)")
19134
- .action(async (opts) => {
20112
+ .description("Show vault details (e.g. vault info <vaultId>)")
20113
+ .argument("<vaultId>", "Vault object ID")
20114
+ .action(async (vaultId) => {
19135
20115
  try {
19136
20116
  const sdk = getSDK();
19137
- await ensureAuth(sdk);
19138
- const vaultAddr = getVaultAddress(opts.address);
19139
- if (!vaultAddr)
19140
- return handleError("Vault address required (--address or VAULT_ADDRESS env)");
19141
- const result = await sdk.getAccountInfo({ parentAddress: vaultAddr });
20117
+ const result = await sdk.getVaultInfo(vaultId);
19142
20118
  if (!result.status || !result.data)
19143
20119
  return handleError(result.error);
19144
20120
  if (isJson(program))
19145
20121
  return printJson(result.data);
19146
20122
  const d = result.data;
19147
20123
  printTable(["Field", "Value"], [
19148
- ["Vault Address", vaultAddr],
19149
- ["Wallet Balance", formatWei(d.walletBalance)],
19150
- ["Account Value", formatWei(d.accountValue)],
19151
- ["Free Collateral", formatWei(d.freeCollateral)],
19152
- ["Total Margin", formatWei(d.totalMargin)],
19153
- ["Unrealized PnL", formatWei(d.totalUnrealizedProfit)],
20124
+ ["Vault ID", vaultId],
20125
+ ["Name", d.name || "-"],
20126
+ ["Creator", d.creator || "-"],
20127
+ ["Trader", d.trader || "-"],
20128
+ ["Deposit Status", d.deposit_status === true || d.deposit_status === "true" ? "Open" : "Closed"],
20129
+ ["Total Shares", formatUsdc(d.total_shares)],
20130
+ ["Max Cap", formatUsdc(d.max_cap)],
20131
+ ["Min Deposit", formatUsdc(d.min_deposit_amount)],
20132
+ ["Creator Min Share Ratio", formatRatio(d.creator_minimum_share_ratio)],
20133
+ ["Creator Profit Share Ratio", formatRatio(d.creator_profit_share_ratio)],
20134
+ ["Auto Close on Withdraw", String(d.auto_close_on_withdraw ?? "-")],
19154
20135
  ]);
19155
20136
  }
19156
20137
  catch (e) {
19157
20138
  handleError(e);
19158
20139
  }
19159
20140
  });
20141
+ // === vault deposit ===
20142
+ vault
20143
+ .command("deposit")
20144
+ .description("Deposit USDC into a vault (e.g. vault deposit <vaultId> <amount>)")
20145
+ .argument("<vaultId>", "Vault object ID")
20146
+ .argument("<amount>", "USDC amount to deposit")
20147
+ .action(async (vaultId, amount) => {
20148
+ try {
20149
+ const sdk = getSDK();
20150
+ console.log(`Depositing ${amount} USDC to vault ${vaultId}...`);
20151
+ const tx = await sdk.depositToVault({ vaultID: vaultId, amount: Number(amount) });
20152
+ printTxResult(program, tx, `Deposited ${amount} USDC.`);
20153
+ }
20154
+ catch (e) {
20155
+ handleError(e);
20156
+ }
20157
+ });
20158
+ // === vault withdraw ===
20159
+ vault
20160
+ .command("withdraw")
20161
+ .description("Request withdrawal from vault (e.g. vault withdraw <vaultId> <shares>)")
20162
+ .argument("<vaultId>", "Vault object ID")
20163
+ .argument("<shares>", "Number of shares to withdraw")
20164
+ .action(async (vaultId, shares) => {
20165
+ try {
20166
+ const sdk = getSDK();
20167
+ console.log(`Requesting withdrawal of ${shares} shares from vault ${vaultId}...`);
20168
+ const tx = await sdk.requestWithdrawFromVault({ vaultID: vaultId, shares: Number(shares) });
20169
+ printTxResult(program, tx, `Withdrawal request submitted for ${shares} shares.`);
20170
+ }
20171
+ catch (e) {
20172
+ handleError(e);
20173
+ }
20174
+ });
20175
+ // === vault fill ===
20176
+ vault
20177
+ .command("fill")
20178
+ .description("Fill pending withdrawal requests (e.g. vault fill <vaultId> <requestIDs...>)")
20179
+ .argument("<vaultId>", "Vault object ID")
20180
+ .argument("<requestIDs...>", "Withdrawal request object IDs")
20181
+ .option("--markets <ids>", "Comma-separated market PerpetualIDs for share price update")
20182
+ .action(async (vaultId, requestIDs, opts) => {
20183
+ try {
20184
+ const sdk = getSDK();
20185
+ const markets = opts.markets ? opts.markets.split(",") : undefined;
20186
+ console.log(`Filling ${requestIDs.length} withdrawal request(s)...`);
20187
+ const tx = await sdk.fillWithdrawalRequests({
20188
+ vaultID: vaultId,
20189
+ withdrawalRequestIDs: requestIDs,
20190
+ markets,
20191
+ });
20192
+ printTxResult(program, tx, `Filled ${requestIDs.length} withdrawal request(s).`);
20193
+ }
20194
+ catch (e) {
20195
+ handleError(e);
20196
+ }
20197
+ });
20198
+ // === vault close ===
20199
+ vault
20200
+ .command("close")
20201
+ .description("Close a vault (e.g. vault close <vaultId>)")
20202
+ .argument("<vaultId>", "Vault object ID")
20203
+ .option("--markets <ids>", "Comma-separated market PerpetualIDs")
20204
+ .action(async (vaultId, opts) => {
20205
+ try {
20206
+ const sdk = getSDK();
20207
+ const markets = opts.markets ? opts.markets.split(",") : undefined;
20208
+ console.log(`Closing vault ${vaultId}...`);
20209
+ const tx = await sdk.closeVault({ vaultID: vaultId, markets });
20210
+ printTxResult(program, tx, "Vault closed.");
20211
+ }
20212
+ catch (e) {
20213
+ handleError(e);
20214
+ }
20215
+ });
20216
+ // === vault remove ===
20217
+ vault
20218
+ .command("remove")
20219
+ .description("Remove a closed vault (e.g. vault remove <vaultId>)")
20220
+ .argument("<vaultId>", "Vault object ID")
20221
+ .action(async (vaultId) => {
20222
+ try {
20223
+ const sdk = getSDK();
20224
+ console.log(`Removing vault ${vaultId}...`);
20225
+ const tx = await sdk.removeVault({ vaultID: vaultId });
20226
+ printTxResult(program, tx, "Vault removed.");
20227
+ }
20228
+ catch (e) {
20229
+ handleError(e);
20230
+ }
20231
+ });
20232
+ // === vault claim ===
20233
+ vault
20234
+ .command("claim")
20235
+ .description("Claim funds from a closed vault (e.g. vault claim <vaultId>)")
20236
+ .argument("<vaultId>", "Vault object ID")
20237
+ .action(async (vaultId) => {
20238
+ try {
20239
+ const sdk = getSDK();
20240
+ console.log(`Claiming funds from vault ${vaultId}...`);
20241
+ const tx = await sdk.claimClosedVaultFunds({ vaultID: vaultId });
20242
+ printTxResult(program, tx, "Funds claimed.");
20243
+ }
20244
+ catch (e) {
20245
+ handleError(e);
20246
+ }
20247
+ });
20248
+ // === vault set-trader ===
20249
+ vault
20250
+ .command("set-trader")
20251
+ .description("Change the trader address (e.g. vault set-trader <vaultId> <address>)")
20252
+ .argument("<vaultId>", "Vault object ID")
20253
+ .argument("<address>", "New trader address")
20254
+ .action(async (vaultId, address) => {
20255
+ try {
20256
+ const sdk = getSDK();
20257
+ const tx = await sdk.setVaultTrader({ vaultID: vaultId, newTrader: address });
20258
+ printTxResult(program, tx, `Trader set to ${address}.`);
20259
+ }
20260
+ catch (e) {
20261
+ handleError(e);
20262
+ }
20263
+ });
20264
+ // === vault set-sub-trader ===
20265
+ vault
20266
+ .command("set-sub-trader")
20267
+ .description("Add or remove a sub-trader (e.g. vault set-sub-trader <vaultId> <address>)")
20268
+ .argument("<vaultId>", "Vault object ID")
20269
+ .argument("<address>", "Sub-trader address")
20270
+ .option("--disable", "Remove sub-trader (default: add)")
20271
+ .action(async (vaultId, address, opts) => {
20272
+ try {
20273
+ const sdk = getSDK();
20274
+ const status = !opts.disable;
20275
+ const tx = await sdk.setVaultSubTrader({ vaultID: vaultId, subTrader: address, status });
20276
+ printTxResult(program, tx, `Sub-trader ${address} ${status ? "added" : "removed"}.`);
20277
+ }
20278
+ catch (e) {
20279
+ handleError(e);
20280
+ }
20281
+ });
20282
+ // === vault set-deposit-status ===
20283
+ vault
20284
+ .command("set-deposit-status")
20285
+ .description("Enable or disable deposits (e.g. vault set-deposit-status <vaultId>)")
20286
+ .argument("<vaultId>", "Vault object ID")
20287
+ .option("--disable", "Disable deposits (default: enable)")
20288
+ .action(async (vaultId, opts) => {
20289
+ try {
20290
+ const sdk = getSDK();
20291
+ const status = !opts.disable;
20292
+ const tx = await sdk.setVaultDepositStatus({ vaultID: vaultId, status });
20293
+ printTxResult(program, tx, `Deposits ${status ? "enabled" : "disabled"}.`);
20294
+ }
20295
+ catch (e) {
20296
+ handleError(e);
20297
+ }
20298
+ });
20299
+ // === vault set-max-cap ===
20300
+ vault
20301
+ .command("set-max-cap")
20302
+ .description("Set maximum USDC cap (e.g. vault set-max-cap <vaultId> <amount>)")
20303
+ .argument("<vaultId>", "Vault object ID")
20304
+ .argument("<amount>", "Max cap in USDC")
20305
+ .action(async (vaultId, amount) => {
20306
+ try {
20307
+ const sdk = getSDK();
20308
+ const tx = await sdk.setVaultMaxCap({ vaultID: vaultId, maxCap: Number(amount) });
20309
+ printTxResult(program, tx, `Max cap set to ${amount} USDC.`);
20310
+ }
20311
+ catch (e) {
20312
+ handleError(e);
20313
+ }
20314
+ });
20315
+ // === vault set-min-deposit ===
20316
+ vault
20317
+ .command("set-min-deposit")
20318
+ .description("Set minimum deposit amount (e.g. vault set-min-deposit <vaultId> <amount>)")
20319
+ .argument("<vaultId>", "Vault object ID")
20320
+ .argument("<amount>", "Min deposit in USDC")
20321
+ .action(async (vaultId, amount) => {
20322
+ try {
20323
+ const sdk = getSDK();
20324
+ const tx = await sdk.setVaultMinDepositAmount({ vaultID: vaultId, minDepositAmount: Number(amount) });
20325
+ printTxResult(program, tx, `Min deposit set to ${amount} USDC.`);
20326
+ }
20327
+ catch (e) {
20328
+ handleError(e);
20329
+ }
20330
+ });
20331
+ // === vault set-auto-close ===
20332
+ vault
20333
+ .command("set-auto-close")
20334
+ .description("Enable or disable auto-close on withdrawal (e.g. vault set-auto-close <vaultId>)")
20335
+ .argument("<vaultId>", "Vault object ID")
20336
+ .option("--disable", "Disable auto-close (default: enable)")
20337
+ .action(async (vaultId, opts) => {
20338
+ try {
20339
+ const sdk = getSDK();
20340
+ const enabled = !opts.disable;
20341
+ const tx = await sdk.setVaultAutoCloseOnWithdraw({ vaultID: vaultId, autoCloseOnWithdraw: enabled });
20342
+ printTxResult(program, tx, `Auto-close on withdraw ${enabled ? "enabled" : "disabled"}.`);
20343
+ }
20344
+ catch (e) {
20345
+ handleError(e);
20346
+ }
20347
+ });
19160
20348
  }
20349
+
19161
20350
  function registerOrdersCommand(program) {
19162
20351
  program
19163
20352
  .command("orders")
@@ -19166,9 +20355,9 @@ function registerOrdersCommand(program) {
19166
20355
  .option("--vault <address>", "Vault address")
19167
20356
  .action(async (opts) => {
19168
20357
  try {
19169
- const sdk = getSDK();
19170
- await ensureAuth(sdk);
19171
- const vault = opts.vault || sdk.address;
20358
+ const vaultIndex = getGlobalVaultIndex(program);
20359
+ const sdk = getSDK(vaultIndex);
20360
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
19172
20361
  const params = {
19173
20362
  parentAddress: vault,
19174
20363
  ...(opts.symbol ? { symbol: opts.symbol } : {}),
@@ -19197,6 +20386,71 @@ function registerOrdersCommand(program) {
19197
20386
  });
19198
20387
  }
19199
20388
 
20389
+ function registerSubAccountCommands(program) {
20390
+ const sub = program.command("sub-account").description("HD-derived sub-account operations");
20391
+ sub
20392
+ .command("set")
20393
+ .description("Set sub-account on-chain (authorize a sub-account address)")
20394
+ .argument("<subAddress>", "Sub-account address to authorize")
20395
+ .action(async (subAddress) => {
20396
+ try {
20397
+ const sdk = getSDK();
20398
+ const tx = await sdk.setSubAccount(subAddress);
20399
+ if (isJson(program))
20400
+ return printJson({ digest: tx?.digest, status: "ok" });
20401
+ console.log(`Sub-account ${subAddress} set. Tx: ${tx?.digest || JSON.stringify(tx)}`);
20402
+ }
20403
+ catch (e) {
20404
+ handleError(e);
20405
+ }
20406
+ });
20407
+ sub
20408
+ .command("setup")
20409
+ .description("Derive sub-account at index and register it on-chain via main account")
20410
+ .argument("<index>", "Vault index to setup (1+)")
20411
+ .action(async (indexStr) => {
20412
+ try {
20413
+ const index = Number(indexStr);
20414
+ if (isNaN(index) || index < 1)
20415
+ return handleError("Vault index must be >= 1");
20416
+ const subAddress = resolveVaultAddress(index);
20417
+ if (!subAddress)
20418
+ return handleError("Could not derive vault address. Is MNEMONIC set?");
20419
+ const sdk = getSDK();
20420
+ console.log(`Registering sub-account ${index} (${subAddress})...`);
20421
+ const tx = await sdk.setSubAccount(subAddress);
20422
+ if (isJson(program))
20423
+ return printJson({ digest: tx?.digest, index, address: subAddress });
20424
+ console.log(`Sub-account ${index} setup complete. Address: ${subAddress}`);
20425
+ console.log(`Tx: ${tx?.digest || JSON.stringify(tx)}`);
20426
+ }
20427
+ catch (e) {
20428
+ handleError(e);
20429
+ }
20430
+ });
20431
+ sub
20432
+ .command("list")
20433
+ .description("List HD-derived sub-account addresses")
20434
+ .option("--count <n>", "Number of addresses to derive", "5")
20435
+ .action(async (opts) => {
20436
+ try {
20437
+ const count = Number(opts.count) || 5;
20438
+ const addresses = listVaultAddresses(count);
20439
+ if (isJson(program))
20440
+ return printJson(addresses);
20441
+ printTable(["Index", "Address", "Path", "Role"], addresses.map((a) => [
20442
+ String(a.index),
20443
+ a.address,
20444
+ a.path,
20445
+ a.index === 0 ? "Main Account" : `Sub-Account ${a.index}`,
20446
+ ]));
20447
+ }
20448
+ catch (e) {
20449
+ handleError(e);
20450
+ }
20451
+ });
20452
+ }
20453
+
19200
20454
  function formatCoinBalance(totalBalance, decimals) {
19201
20455
  const bn = new BigNumber__default["default"](totalBalance);
19202
20456
  if (bn.isNaN() || bn.isZero())
@@ -19209,14 +20463,18 @@ function registerBalanceCommand(program) {
19209
20463
  .description("Show on-chain coin balances for the current wallet")
19210
20464
  .action(async () => {
19211
20465
  try {
19212
- const sdk = getSDK();
19213
- const result = await sdk.getAllBalances();
20466
+ const vaultIndex = getGlobalVaultIndex(program);
20467
+ const sdk = getSDK(vaultIndex);
20468
+ const vaultAddr = resolveVaultAddress(vaultIndex);
20469
+ const result = await sdk.getAllBalances(vaultAddr);
19214
20470
  if (!result.status || !result.data)
19215
20471
  return handleError(result.error);
19216
20472
  if (isJson(program))
19217
20473
  return printJson(result.data);
19218
20474
  if (!result.data.length)
19219
20475
  return console.log("No coins found.");
20476
+ if (vaultAddr)
20477
+ console.log(`Balances for vault: ${vaultAddr}`);
19220
20478
  const rows = [];
19221
20479
  for (const b of result.data) {
19222
20480
  const meta = await sdk.getCoinMetadata(b.coinType);
@@ -19234,19 +20492,24 @@ function registerBalanceCommand(program) {
19234
20492
 
19235
20493
  const program = new commander.Command();
19236
20494
  program
19237
- .name("dipcoin")
20495
+ .name("dipcoin-cli")
19238
20496
  .description("DipCoin Perpetual Trading CLI")
19239
20497
  .version("0.5.0")
19240
- .option("--json", "Output in JSON format");
20498
+ .option("--json", "Output in JSON format")
20499
+ .option("--vault-index <n>", "Vault index (1+ for HD-derived sub-accounts)");
19241
20500
  registerAccountCommands(program);
19242
20501
  registerTradeCommands(program);
19243
20502
  registerPositionCommands(program);
19244
20503
  registerMarketCommands(program);
19245
20504
  registerHistoryCommands(program);
19246
20505
  registerVaultCommands(program);
20506
+ registerSubAccountCommands(program);
19247
20507
  registerOrdersCommand(program);
19248
20508
  registerBalanceCommand(program);
19249
20509
  program.parseAsync(process.argv).catch((err) => {
19250
20510
  console.error("Error:", err.message || err);
19251
20511
  process.exit(1);
19252
20512
  });
20513
+
20514
+ exports.getGlobalVaultIndex = getGlobalVaultIndex;
20515
+ //# sourceMappingURL=cli.cjs.map