dipcoin-cli 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) 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 +1179 -217
  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/sdk/sdk.d.ts +117 -1
  35. package/dist/src/sdk/sdk.d.ts.map +1 -1
  36. package/dist/src/types/index.d.ts +6 -1
  37. package/dist/src/types/index.d.ts.map +1 -1
  38. package/dist/src/utils/jwt-cache.d.ts +14 -0
  39. package/dist/src/utils/jwt-cache.d.ts.map +1 -0
  40. package/package.json +7 -10
package/dist/cli.cjs CHANGED
@@ -1,27 +1,31 @@
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');
8
+ var path = require('path');
9
+ var os = require('os');
10
+ var fs = require('fs');
11
+ var ed25519 = require('@mysten/sui/keypairs/ed25519');
6
12
  var perpTsLibrary = require('@dipcoinlab/perp-ts-library');
7
13
  var transactions = require('@mysten/sui/transactions');
8
14
  var BigNumber = require('bignumber.js');
9
15
  var pythSuiJs = require('@pythnetwork/pyth-sui-js');
10
16
  var child_process = require('child_process');
11
- var ed25519 = require('@mysten/sui/keypairs/ed25519');
12
17
  var utils = require('@mysten/sui/utils');
13
18
  var buffer = require('buffer');
14
- var fs = require('fs');
15
- var path = require('path');
16
19
  var url = require('url');
17
20
  var Table = require('cli-table3');
18
21
 
19
22
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
20
23
 
21
24
  var dotenv__default = /*#__PURE__*/_interopDefaultLegacy(dotenv);
22
- var BigNumber__default = /*#__PURE__*/_interopDefaultLegacy(BigNumber);
23
- var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
24
25
  var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
26
+ var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
27
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
28
+ var BigNumber__default = /*#__PURE__*/_interopDefaultLegacy(BigNumber);
25
29
  var Table__default = /*#__PURE__*/_interopDefaultLegacy(Table);
26
30
 
27
31
  // Copyright (c) 2025 Dipcoin LLC
@@ -41,6 +45,7 @@ function initSDKOptions(options) {
41
45
  apiBaseUrl: options.apiBaseUrl || DEFAULT_API_URLS[options.network],
42
46
  network: options.network,
43
47
  customRpc: options.customRpc,
48
+ subAccountKey: options.subAccountKey,
44
49
  };
45
50
  }
46
51
 
@@ -9366,6 +9371,7 @@ const API_ENDPOINTS = {
9366
9371
  FUNDING_SETTLEMENTS: "/api/perp-trade-api/history/funding-settlements",
9367
9372
  BALANCE_CHANGES: "/api/perp-trade-api/history/balance-changes",
9368
9373
  ORACLE: "/api/perp-market-api/oracle",
9374
+ VAULTS_BY_CREATOR: "/api/perp-vault-api/public/vaults/by-creator",
9369
9375
  };
9370
9376
  /**
9371
9377
  * Onboarding message for authentication
@@ -16449,7 +16455,7 @@ function findPackageRoot(startDir) {
16449
16455
  if (fs__default["default"].existsSync(packageJsonPath)) {
16450
16456
  try {
16451
16457
  const packageJson = JSON.parse(fs__default["default"].readFileSync(packageJsonPath, "utf-8"));
16452
- if (packageJson.name === "@dipcoinlab/perp-client-ts") {
16458
+ if (packageJson.name === "dipcoin-cli") {
16453
16459
  return currentDir;
16454
16460
  }
16455
16461
  }
@@ -16514,6 +16520,78 @@ function readFile(filePath) {
16514
16520
  return config;
16515
16521
  }
16516
16522
 
16523
+ // Copyright (c) 2025 Dipcoin LLC
16524
+ const CACHE_DIR = path__default["default"].join(os__default["default"].homedir(), ".config", "dipcoin", "jwt");
16525
+ /** Safety margin: treat token as expired 60s before actual expiry */
16526
+ const EXPIRY_MARGIN_S = 60;
16527
+ function getCachePath(address) {
16528
+ return path__default["default"].join(CACHE_DIR, `${address}.json`);
16529
+ }
16530
+ function parseJwtExp(token) {
16531
+ try {
16532
+ const parts = token.split(".");
16533
+ if (parts.length !== 3)
16534
+ return null;
16535
+ const payload = JSON.parse(Buffer.from(parts[1], "base64").toString());
16536
+ return typeof payload.exp === "number" ? payload.exp : null;
16537
+ }
16538
+ catch {
16539
+ return null;
16540
+ }
16541
+ }
16542
+ /**
16543
+ * Load a cached JWT for the given wallet address.
16544
+ * Returns the token string if valid and not expired, otherwise null.
16545
+ */
16546
+ function loadCachedJwt(address) {
16547
+ try {
16548
+ const filePath = getCachePath(address);
16549
+ if (!fs__default["default"].existsSync(filePath))
16550
+ return null;
16551
+ const raw = fs__default["default"].readFileSync(filePath, "utf-8");
16552
+ const entry = JSON.parse(raw);
16553
+ const now = Math.floor(Date.now() / 1000);
16554
+ if (entry.exp - EXPIRY_MARGIN_S > now) {
16555
+ return entry.token;
16556
+ }
16557
+ // Expired, clean up
16558
+ fs__default["default"].unlinkSync(filePath);
16559
+ return null;
16560
+ }
16561
+ catch {
16562
+ return null;
16563
+ }
16564
+ }
16565
+ /**
16566
+ * Save a JWT to the local cache.
16567
+ */
16568
+ function saveCachedJwt(address, token) {
16569
+ try {
16570
+ const exp = parseJwtExp(token);
16571
+ if (!exp)
16572
+ return;
16573
+ fs__default["default"].mkdirSync(CACHE_DIR, { recursive: true });
16574
+ const entry = { token, exp };
16575
+ fs__default["default"].writeFileSync(getCachePath(address), JSON.stringify(entry), { mode: 0o600 });
16576
+ }
16577
+ catch {
16578
+ // Non-fatal: cache write failure is acceptable
16579
+ }
16580
+ }
16581
+ /**
16582
+ * Remove cached JWT for the given address.
16583
+ */
16584
+ function clearCachedJwt(address) {
16585
+ try {
16586
+ const filePath = getCachePath(address);
16587
+ if (fs__default["default"].existsSync(filePath))
16588
+ fs__default["default"].unlinkSync(filePath);
16589
+ }
16590
+ catch {
16591
+ // ignore
16592
+ }
16593
+ }
16594
+
16517
16595
  // Copyright (c) 2025 Dipcoin LLC
16518
16596
  /**
16519
16597
  * DipCoin Perpetual Trading SDK
@@ -16554,7 +16632,12 @@ class DipCoinPerpSDK {
16554
16632
  }
16555
16633
  // Initialize sub-account keypair if provided
16556
16634
  if (options.subAccountKey) {
16557
- this.subKeypair = fromExportedKeypair(options.subAccountKey);
16635
+ if (typeof options.subAccountKey === "string") {
16636
+ this.subKeypair = fromExportedKeypair(options.subAccountKey);
16637
+ }
16638
+ else {
16639
+ this.subKeypair = options.subAccountKey;
16640
+ }
16558
16641
  this.subAddress = this.subKeypair.getPublicKey().toSuiAddress();
16559
16642
  }
16560
16643
  // Get wallet address
@@ -16593,30 +16676,27 @@ class DipCoinPerpSDK {
16593
16676
  */
16594
16677
  async authenticate() {
16595
16678
  try {
16596
- // If already authenticated and token exists, return it
16679
+ // If already authenticated in this process, return it
16597
16680
  if (this.jwtToken) {
16598
- return {
16599
- status: true,
16600
- data: this.jwtToken,
16601
- };
16681
+ return { status: true, data: this.jwtToken };
16682
+ }
16683
+ // Try loading from local cache
16684
+ const cached = loadCachedJwt(this.walletAddress);
16685
+ if (cached) {
16686
+ this.jwtToken = cached;
16687
+ this.httpClient.setAuthToken(cached);
16688
+ return { status: true, data: cached };
16602
16689
  }
16603
16690
  // Prevent concurrent authentication requests
16604
16691
  if (this.isAuthenticating) {
16605
- // Wait for ongoing authentication
16606
16692
  await new Promise((resolve) => setTimeout(resolve, 100));
16607
16693
  if (this.jwtToken) {
16608
- return {
16609
- status: true,
16610
- data: this.jwtToken,
16611
- };
16694
+ return { status: true, data: this.jwtToken };
16612
16695
  }
16613
16696
  }
16614
16697
  this.isAuthenticating = true;
16615
- // 1. Prepare onboarding message
16616
16698
  const messageBytes = new TextEncoder().encode(ONBOARDING_MESSAGE);
16617
- // 2. Sign the message
16618
16699
  const signature = await signMessage(this.keypair, messageBytes);
16619
- // 3. Call authorize endpoint to get JWT token
16620
16700
  const response = await this.httpClient.post(API_ENDPOINTS.AUTHORIZE, {
16621
16701
  userAddress: this.walletAddress,
16622
16702
  isTermAccepted: true,
@@ -16625,26 +16705,18 @@ class DipCoinPerpSDK {
16625
16705
  if (response.code === 200 && response.data?.token) {
16626
16706
  this.jwtToken = response.data.token;
16627
16707
  this.httpClient.setAuthToken(this.jwtToken);
16708
+ saveCachedJwt(this.walletAddress, this.jwtToken);
16628
16709
  this.isAuthenticating = false;
16629
- return {
16630
- status: true,
16631
- data: this.jwtToken,
16632
- };
16710
+ return { status: true, data: this.jwtToken };
16633
16711
  }
16634
16712
  else {
16635
16713
  this.isAuthenticating = false;
16636
- return {
16637
- status: false,
16638
- error: response.message || "Failed to authenticate",
16639
- };
16714
+ return { status: false, error: response.message || "Failed to authenticate" };
16640
16715
  }
16641
16716
  }
16642
16717
  catch (error) {
16643
16718
  this.isAuthenticating = false;
16644
- return {
16645
- status: false,
16646
- error: formatError(error),
16647
- };
16719
+ return { status: false, error: formatError(error) };
16648
16720
  }
16649
16721
  }
16650
16722
  /**
@@ -16655,10 +16727,17 @@ class DipCoinPerpSDK {
16655
16727
  if (!this.subKeypair) {
16656
16728
  return { status: false, error: "Sub-account keypair not configured" };
16657
16729
  }
16730
+ const subAddress = this.subKeypair.getPublicKey().toSuiAddress();
16658
16731
  try {
16659
16732
  if (this.subJwtToken) {
16660
16733
  return { status: true, data: this.subJwtToken };
16661
16734
  }
16735
+ // Try loading from local cache
16736
+ const cached = loadCachedJwt(subAddress);
16737
+ if (cached) {
16738
+ this.subJwtToken = cached;
16739
+ return { status: true, data: cached };
16740
+ }
16662
16741
  if (this.isSubAuthenticating) {
16663
16742
  await new Promise((resolve) => setTimeout(resolve, 100));
16664
16743
  if (this.subJwtToken) {
@@ -16668,7 +16747,6 @@ class DipCoinPerpSDK {
16668
16747
  this.isSubAuthenticating = true;
16669
16748
  const messageBytes = new TextEncoder().encode(ONBOARDING_MESSAGE);
16670
16749
  const signature = await signMessage(this.subKeypair, messageBytes);
16671
- const subAddress = this.subKeypair.getPublicKey().toSuiAddress();
16672
16750
  const response = await this.httpClient.post(API_ENDPOINTS.AUTHORIZE, {
16673
16751
  userAddress: subAddress,
16674
16752
  isTermAccepted: true,
@@ -16676,6 +16754,7 @@ class DipCoinPerpSDK {
16676
16754
  });
16677
16755
  if (response.code === 200 && response.data?.token) {
16678
16756
  this.subJwtToken = response.data.token;
16757
+ saveCachedJwt(subAddress, this.subJwtToken);
16679
16758
  this.isSubAuthenticating = false;
16680
16759
  return { status: true, data: this.subJwtToken };
16681
16760
  }
@@ -16732,6 +16811,10 @@ class DipCoinPerpSDK {
16732
16811
  * Clear JWT token (logout)
16733
16812
  */
16734
16813
  clearAuth() {
16814
+ if (this.jwtToken)
16815
+ clearCachedJwt(this.walletAddress);
16816
+ if (this.subJwtToken && this.subAddress)
16817
+ clearCachedJwt(this.subAddress);
16735
16818
  this.jwtToken = undefined;
16736
16819
  this.subJwtToken = undefined;
16737
16820
  this.httpClient.setAuthToken("");
@@ -17231,7 +17314,7 @@ class DipCoinPerpSDK {
17231
17314
  const tpOrder = {
17232
17315
  market,
17233
17316
  creator: this.walletAddress,
17234
- isLong,
17317
+ isLong: !isLong,
17235
17318
  reduceOnly,
17236
17319
  postOnly,
17237
17320
  orderbookOnly,
@@ -17276,7 +17359,7 @@ class DipCoinPerpSDK {
17276
17359
  const slOrder = {
17277
17360
  market,
17278
17361
  creator: this.walletAddress,
17279
- isLong,
17362
+ isLong: !isLong,
17280
17363
  reduceOnly,
17281
17364
  postOnly,
17282
17365
  orderbookOnly,
@@ -17552,36 +17635,7 @@ class DipCoinPerpSDK {
17552
17635
  */
17553
17636
  async getTradingPairs() {
17554
17637
  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
17638
  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
17639
  if (response.code === 200) {
17586
17640
  const pairs = Array.isArray(response.data) ? response.data : response.data?.data || [];
17587
17641
  return {
@@ -17715,12 +17769,6 @@ class DipCoinPerpSDK {
17715
17769
  error: "Symbol is required",
17716
17770
  };
17717
17771
  }
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
17772
  const params = {
17725
17773
  symbol,
17726
17774
  };
@@ -17873,12 +17921,6 @@ class DipCoinPerpSDK {
17873
17921
  error: "Symbol is required",
17874
17922
  };
17875
17923
  }
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
17924
  const params = {
17883
17925
  symbol,
17884
17926
  };
@@ -18078,6 +18120,10 @@ class DipCoinPerpSDK {
18078
18120
  queryParams.pageSize = params.pageSize;
18079
18121
  if (params?.parentAddress)
18080
18122
  queryParams.parentAddress = params.parentAddress;
18123
+ if (params?.beginTime)
18124
+ queryParams.beginTime = params.beginTime;
18125
+ if (params?.endTime)
18126
+ queryParams.endTime = params.endTime;
18081
18127
  let response = await this.httpClient.get(API_ENDPOINTS.HISTORY_ORDERS, { params: queryParams });
18082
18128
  if (response.code === 1000) {
18083
18129
  this.clearAuth();
@@ -18117,6 +18163,8 @@ class DipCoinPerpSDK {
18117
18163
  queryParams.pageSize = params.pageSize;
18118
18164
  if (params?.parentAddress)
18119
18165
  queryParams.parentAddress = params.parentAddress;
18166
+ if (params?.beginTime)
18167
+ queryParams.beginTime = params.beginTime;
18120
18168
  let response = await this.httpClient.get(API_ENDPOINTS.FUNDING_SETTLEMENTS, { params: queryParams });
18121
18169
  if (response.code === 1000) {
18122
18170
  this.clearAuth();
@@ -18154,6 +18202,8 @@ class DipCoinPerpSDK {
18154
18202
  queryParams.pageSize = params.pageSize;
18155
18203
  if (params?.parentAddress)
18156
18204
  queryParams.parentAddress = params.parentAddress;
18205
+ if (params?.beginTime)
18206
+ queryParams.beginTime = params.beginTime;
18157
18207
  let response = await this.httpClient.get(API_ENDPOINTS.BALANCE_CHANGES, { params: queryParams });
18158
18208
  if (response.code === 1000) {
18159
18209
  this.clearAuth();
@@ -18184,7 +18234,6 @@ class DipCoinPerpSDK {
18184
18234
  if (!symbol) {
18185
18235
  return { status: false, error: "Symbol is required" };
18186
18236
  }
18187
- await this.authenticate().catch(() => { });
18188
18237
  const response = await this.httpClient.get(API_ENDPOINTS.ORACLE, { params: { symbol } });
18189
18238
  if (response.code === 200 && response.data !== undefined) {
18190
18239
  return { status: true, data: String(response.data) };
@@ -18470,9 +18519,9 @@ class DipCoinPerpSDK {
18470
18519
  * Get all coin balances on-chain for the current wallet address
18471
18520
  * @returns Array of coin balances with coinType and totalBalance
18472
18521
  */
18473
- async getAllBalances() {
18522
+ async getAllBalances(owner) {
18474
18523
  try {
18475
- const balances = await this.suiClient.getAllBalances({ owner: this.walletAddress });
18524
+ const balances = await this.suiClient.getAllBalances({ owner: owner || this.walletAddress });
18476
18525
  return { status: true, data: balances };
18477
18526
  }
18478
18527
  catch (error) {
@@ -18495,6 +18544,416 @@ class DipCoinPerpSDK {
18495
18544
  return { status: false, error: formatError(error) };
18496
18545
  }
18497
18546
  }
18547
+ // =====================================================
18548
+ // Vault (on-chain contract) Operations
18549
+ // =====================================================
18550
+ //
18551
+ // Vault functions live in a SEPARATE package from the exchange.
18552
+ // The library's ExchangeOnChain uses getPackageID() (exchange pkg)
18553
+ // for vault moveCall targets, causing TypeMismatch errors.
18554
+ // We build vault transactions manually with the correct vault package.
18555
+ // =====================================================
18556
+ getVaultPackageId() {
18557
+ const pkg = this.deploymentConfig?.vaultPackage;
18558
+ if (!pkg)
18559
+ throw new Error("Deployment config missing vaultPackage");
18560
+ return pkg;
18561
+ }
18562
+ getVaultConfigId() {
18563
+ const id = this.deploymentConfig?.objects?.VaultConfig?.id;
18564
+ if (!id)
18565
+ throw new Error("Deployment config missing VaultConfig id");
18566
+ return id;
18567
+ }
18568
+ getBankId() {
18569
+ const id = this.deploymentConfig?.objects?.Bank?.id;
18570
+ if (!id)
18571
+ throw new Error("Deployment config missing Bank id");
18572
+ return id;
18573
+ }
18574
+ getTxIndexerId() {
18575
+ const id = this.deploymentConfig?.objects?.TxIndexer?.id;
18576
+ if (!id)
18577
+ throw new Error("Deployment config missing TxIndexer id");
18578
+ return id;
18579
+ }
18580
+ getSubAccountsId() {
18581
+ const id = this.deploymentConfig?.objects?.SubAccounts?.id;
18582
+ if (!id)
18583
+ throw new Error("Deployment config missing SubAccounts id");
18584
+ return id;
18585
+ }
18586
+ getCurrencyType() {
18587
+ const dt = this.deploymentConfig?.objects?.Currency?.dataType;
18588
+ if (!dt)
18589
+ throw new Error("Deployment config missing Currency dataType");
18590
+ return dt;
18591
+ }
18592
+ getMarketSymbols() {
18593
+ return Object.keys(this.deploymentConfig?.markets || {});
18594
+ }
18595
+ getVaultPerpetualId(symbol) {
18596
+ const id = this.deploymentConfig?.markets?.[symbol]?.Objects?.Perpetual?.id;
18597
+ if (!id)
18598
+ throw new Error(`Deployment config missing Perpetual id for ${symbol}`);
18599
+ return id;
18600
+ }
18601
+ getVaultPriceInfoObjectId(symbol) {
18602
+ const id = this.deploymentConfig?.markets?.[symbol]?.Objects?.PriceInfoObject?.id;
18603
+ if (!id)
18604
+ throw new Error(`Deployment config missing PriceInfoObject id for ${symbol}`);
18605
+ return id;
18606
+ }
18607
+ async buildVaultNavTransaction(vaultID, markets, tx) {
18608
+ const t = tx || new transactions.Transaction();
18609
+ const vaultPkg = this.getVaultPackageId();
18610
+ const currencyType = this.getCurrencyType();
18611
+ const symbols = markets && markets.length > 0 ? markets : this.getMarketSymbols();
18612
+ // On testnet, prepend price oracle updates so PriceInfoObjects are fresh
18613
+ if (this.options.network === "testnet" && this.transactionBuilder) {
18614
+ const prices = [];
18615
+ for (const sym of symbols) {
18616
+ try {
18617
+ const oraclePrice = await this.exchangeOnChain.getOraclePrice(sym);
18618
+ if (oraclePrice !== undefined && oraclePrice !== null) {
18619
+ prices.push({ price: Number(oraclePrice), market: sym });
18620
+ }
18621
+ }
18622
+ catch {
18623
+ // skip symbols where oracle price is unavailable
18624
+ }
18625
+ }
18626
+ if (prices.length > 0) {
18627
+ this.transactionBuilder.price_info_batchSetOraclePriceTx({ prices }, t);
18628
+ }
18629
+ }
18630
+ const [nav] = t.moveCall({
18631
+ target: `${vaultPkg}::vault::new_vault_nav`,
18632
+ arguments: [
18633
+ t.object(this.getDeploymentProtocolConfigId()),
18634
+ t.object(this.getVaultConfigId()),
18635
+ t.object(vaultID),
18636
+ t.object(this.getBankId()),
18637
+ ],
18638
+ typeArguments: [currencyType],
18639
+ });
18640
+ for (const sym of symbols) {
18641
+ const perpId = this.getVaultPerpetualId(sym);
18642
+ const priceInfoId = this.getVaultPriceInfoObjectId(sym);
18643
+ t.moveCall({
18644
+ target: `${vaultPkg}::vault::compute_perpetual_position_value`,
18645
+ arguments: [nav, t.object("0x6"), t.object(perpId), t.object(priceInfoId)],
18646
+ typeArguments: [],
18647
+ });
18648
+ }
18649
+ return { nav, tx: t };
18650
+ }
18651
+ async signAndExecuteVaultTx(tx) {
18652
+ tx.setSender(this.keypair.getPublicKey().toSuiAddress());
18653
+ return await this.suiClient.signAndExecuteTransaction({
18654
+ signer: this.keypair,
18655
+ transaction: tx,
18656
+ options: { showEffects: true, showObjectChanges: true },
18657
+ });
18658
+ }
18659
+ /**
18660
+ * Create a new on-chain vault
18661
+ */
18662
+ async createVault(args) {
18663
+ const tx = new transactions.Transaction();
18664
+ const vaultPkg = this.getVaultPackageId();
18665
+ tx.moveCall({
18666
+ target: `${vaultPkg}::vault::create_vault`,
18667
+ arguments: [
18668
+ tx.object(this.getDeploymentProtocolConfigId()),
18669
+ tx.object("0x6"),
18670
+ tx.object(this.getVaultConfigId()),
18671
+ tx.object(this.getSubAccountsId()),
18672
+ tx.object(this.getBankId()),
18673
+ tx.object(this.getTxIndexerId()),
18674
+ tx.pure.string(args.name),
18675
+ tx.pure.address(args.trader),
18676
+ tx.pure.u128(formatNormalToWei(args.maxCap)),
18677
+ tx.pure.u128(formatNormalToWei(args.minDepositAmount)),
18678
+ tx.pure.u128(args.creatorMinimumShareRatio),
18679
+ tx.pure.u128(args.creatorProfitShareRatio),
18680
+ tx.pure.u128(formatNormalToWei(args.initialAmount)),
18681
+ ],
18682
+ typeArguments: [this.getCurrencyType()],
18683
+ });
18684
+ return this.signAndExecuteVaultTx(tx);
18685
+ }
18686
+ /**
18687
+ * Deposit USDC into a vault
18688
+ */
18689
+ async depositToVault(args) {
18690
+ const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
18691
+ const vaultPkg = this.getVaultPackageId();
18692
+ tx.moveCall({
18693
+ target: `${vaultPkg}::vault::deposit`,
18694
+ arguments: [
18695
+ tx.object(this.getDeploymentProtocolConfigId()),
18696
+ tx.object(this.getVaultConfigId()),
18697
+ tx.object("0x6"),
18698
+ tx.object(this.getBankId()),
18699
+ tx.object(this.getTxIndexerId()),
18700
+ tx.object(args.vaultID),
18701
+ nav,
18702
+ tx.pure.u128(formatNormalToWei(args.amount)),
18703
+ ],
18704
+ typeArguments: [this.getCurrencyType()],
18705
+ });
18706
+ return this.signAndExecuteVaultTx(tx);
18707
+ }
18708
+ /**
18709
+ * Request withdrawal from a vault (by share amount)
18710
+ */
18711
+ async requestWithdrawFromVault(args) {
18712
+ const tx = new transactions.Transaction();
18713
+ const vaultPkg = this.getVaultPackageId();
18714
+ tx.moveCall({
18715
+ target: `${vaultPkg}::vault::request_withdraw`,
18716
+ arguments: [
18717
+ tx.object(this.getDeploymentProtocolConfigId()),
18718
+ tx.object(this.getVaultConfigId()),
18719
+ tx.object("0x6"),
18720
+ tx.object(args.vaultID),
18721
+ tx.pure.u128(formatNormalToWei(args.shares)),
18722
+ ],
18723
+ typeArguments: [this.getCurrencyType()],
18724
+ });
18725
+ return this.signAndExecuteVaultTx(tx);
18726
+ }
18727
+ /**
18728
+ * Fill pending withdrawal requests (creator/operator only)
18729
+ */
18730
+ async fillWithdrawalRequests(args) {
18731
+ const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
18732
+ const vaultPkg = this.getVaultPackageId();
18733
+ const requestVec = tx.makeMoveVec({
18734
+ elements: args.withdrawalRequestIDs.map((id) => tx.object(id)),
18735
+ });
18736
+ tx.moveCall({
18737
+ target: `${vaultPkg}::vault::fill_withdrawal_requests`,
18738
+ arguments: [
18739
+ tx.object(this.getDeploymentProtocolConfigId()),
18740
+ tx.object(this.getVaultConfigId()),
18741
+ tx.object("0x6"),
18742
+ tx.object(this.getBankId()),
18743
+ tx.object(this.getTxIndexerId()),
18744
+ tx.object(args.vaultID),
18745
+ nav,
18746
+ requestVec,
18747
+ ],
18748
+ typeArguments: [this.getCurrencyType()],
18749
+ });
18750
+ return this.signAndExecuteVaultTx(tx);
18751
+ }
18752
+ /**
18753
+ * Close a vault (creator only)
18754
+ */
18755
+ async closeVault(args) {
18756
+ const { nav, tx } = await this.buildVaultNavTransaction(args.vaultID, args.markets);
18757
+ const vaultPkg = this.getVaultPackageId();
18758
+ tx.moveCall({
18759
+ target: `${vaultPkg}::vault::close_vault_v2`,
18760
+ arguments: [
18761
+ tx.object(this.getDeploymentProtocolConfigId()),
18762
+ tx.object(this.getVaultConfigId()),
18763
+ tx.object(this.getSubAccountsId()),
18764
+ tx.object(args.vaultID),
18765
+ tx.object(this.getBankId()),
18766
+ tx.object("0x6"),
18767
+ nav,
18768
+ ],
18769
+ typeArguments: [this.getCurrencyType()],
18770
+ });
18771
+ return this.signAndExecuteVaultTx(tx);
18772
+ }
18773
+ /**
18774
+ * Remove a closed vault (after all funds claimed)
18775
+ */
18776
+ async removeVault(args) {
18777
+ const tx = new transactions.Transaction();
18778
+ const vaultPkg = this.getVaultPackageId();
18779
+ tx.moveCall({
18780
+ target: `${vaultPkg}::vault::remove_vault`,
18781
+ arguments: [
18782
+ tx.object(this.getDeploymentProtocolConfigId()),
18783
+ tx.object(this.getVaultConfigId()),
18784
+ tx.object(this.getBankId()),
18785
+ tx.object(args.vaultID),
18786
+ ],
18787
+ typeArguments: [this.getCurrencyType()],
18788
+ });
18789
+ return this.signAndExecuteVaultTx(tx);
18790
+ }
18791
+ /**
18792
+ * Claim funds from a closed vault
18793
+ */
18794
+ async claimClosedVaultFunds(args) {
18795
+ const tx = new transactions.Transaction();
18796
+ const vaultPkg = this.getVaultPackageId();
18797
+ tx.moveCall({
18798
+ target: `${vaultPkg}::vault::claim_closed_vault_funds`,
18799
+ arguments: [
18800
+ tx.object(this.getDeploymentProtocolConfigId()),
18801
+ tx.object(this.getVaultConfigId()),
18802
+ tx.object(this.getBankId()),
18803
+ tx.object(this.getTxIndexerId()),
18804
+ tx.object(args.vaultID),
18805
+ ],
18806
+ typeArguments: [this.getCurrencyType()],
18807
+ });
18808
+ return this.signAndExecuteVaultTx(tx);
18809
+ }
18810
+ /**
18811
+ * Set the trader address for a vault (creator only)
18812
+ */
18813
+ async setVaultTrader(args) {
18814
+ const tx = new transactions.Transaction();
18815
+ const vaultPkg = this.getVaultPackageId();
18816
+ tx.moveCall({
18817
+ target: `${vaultPkg}::vault::set_trader`,
18818
+ arguments: [
18819
+ tx.object(this.getDeploymentProtocolConfigId()),
18820
+ tx.object(this.getVaultConfigId()),
18821
+ tx.object(args.vaultID),
18822
+ tx.object(this.getSubAccountsId()),
18823
+ tx.pure.address(args.newTrader),
18824
+ ],
18825
+ typeArguments: [this.getCurrencyType()],
18826
+ });
18827
+ return this.signAndExecuteVaultTx(tx);
18828
+ }
18829
+ /**
18830
+ * Add or remove a sub-trader for a vault
18831
+ */
18832
+ async setVaultSubTrader(args) {
18833
+ const tx = new transactions.Transaction();
18834
+ const vaultPkg = this.getVaultPackageId();
18835
+ tx.moveCall({
18836
+ target: `${vaultPkg}::vault::set_sub_trader`,
18837
+ arguments: [
18838
+ tx.object(this.getDeploymentProtocolConfigId()),
18839
+ tx.object(this.getVaultConfigId()),
18840
+ tx.object(args.vaultID),
18841
+ tx.object(this.getSubAccountsId()),
18842
+ tx.pure.address(args.subTrader),
18843
+ tx.pure.bool(args.status),
18844
+ ],
18845
+ typeArguments: [this.getCurrencyType()],
18846
+ });
18847
+ return this.signAndExecuteVaultTx(tx);
18848
+ }
18849
+ /**
18850
+ * Enable or disable deposits to a vault (creator only)
18851
+ */
18852
+ async setVaultDepositStatus(args) {
18853
+ const tx = new transactions.Transaction();
18854
+ const vaultPkg = this.getVaultPackageId();
18855
+ tx.moveCall({
18856
+ target: `${vaultPkg}::vault::set_deposit_status`,
18857
+ arguments: [
18858
+ tx.object(this.getDeploymentProtocolConfigId()),
18859
+ tx.object(this.getVaultConfigId()),
18860
+ tx.object(args.vaultID),
18861
+ tx.pure.bool(args.status),
18862
+ ],
18863
+ typeArguments: [this.getCurrencyType()],
18864
+ });
18865
+ return this.signAndExecuteVaultTx(tx);
18866
+ }
18867
+ /**
18868
+ * Set maximum USDC cap for a vault (creator only)
18869
+ */
18870
+ async setVaultMaxCap(args) {
18871
+ const tx = new transactions.Transaction();
18872
+ const vaultPkg = this.getVaultPackageId();
18873
+ tx.moveCall({
18874
+ target: `${vaultPkg}::vault::set_max_cap`,
18875
+ arguments: [
18876
+ tx.object(this.getDeploymentProtocolConfigId()),
18877
+ tx.object(this.getVaultConfigId()),
18878
+ tx.object(args.vaultID),
18879
+ tx.pure.u128(formatNormalToWei(args.maxCap)),
18880
+ ],
18881
+ typeArguments: [this.getCurrencyType()],
18882
+ });
18883
+ return this.signAndExecuteVaultTx(tx);
18884
+ }
18885
+ /**
18886
+ * Set minimum deposit amount for a vault (creator only)
18887
+ */
18888
+ async setVaultMinDepositAmount(args) {
18889
+ const tx = new transactions.Transaction();
18890
+ const vaultPkg = this.getVaultPackageId();
18891
+ tx.moveCall({
18892
+ target: `${vaultPkg}::vault::set_min_deposit_amount`,
18893
+ arguments: [
18894
+ tx.object(this.getDeploymentProtocolConfigId()),
18895
+ tx.object(this.getVaultConfigId()),
18896
+ tx.object(args.vaultID),
18897
+ tx.pure.u128(formatNormalToWei(args.minDepositAmount)),
18898
+ ],
18899
+ typeArguments: [this.getCurrencyType()],
18900
+ });
18901
+ return this.signAndExecuteVaultTx(tx);
18902
+ }
18903
+ /**
18904
+ * Enable or disable auto-close on withdrawal (creator only)
18905
+ */
18906
+ async setVaultAutoCloseOnWithdraw(args) {
18907
+ const tx = new transactions.Transaction();
18908
+ const vaultPkg = this.getVaultPackageId();
18909
+ tx.moveCall({
18910
+ target: `${vaultPkg}::vault::set_auto_close_on_withdraw`,
18911
+ arguments: [
18912
+ tx.object(this.getDeploymentProtocolConfigId()),
18913
+ tx.object(this.getVaultConfigId()),
18914
+ tx.object(args.vaultID),
18915
+ tx.pure.bool(args.autoCloseOnWithdraw),
18916
+ ],
18917
+ typeArguments: [this.getCurrencyType()],
18918
+ });
18919
+ return this.signAndExecuteVaultTx(tx);
18920
+ }
18921
+ /**
18922
+ * Read vault object data from chain
18923
+ */
18924
+ async getVaultInfo(vaultID) {
18925
+ try {
18926
+ const obj = await this.suiClient.getObject({
18927
+ id: vaultID,
18928
+ options: { showContent: true, showType: true },
18929
+ });
18930
+ if (!obj.data?.content || obj.data.content.dataType !== "moveObject") {
18931
+ return { status: false, error: "Vault object not found or not a Move object" };
18932
+ }
18933
+ return { status: true, data: obj.data.content.fields };
18934
+ }
18935
+ catch (error) {
18936
+ return { status: false, error: formatError(error) };
18937
+ }
18938
+ }
18939
+ /**
18940
+ * List vaults created by a specific address via server API.
18941
+ */
18942
+ async listVaults(creatorAddress) {
18943
+ try {
18944
+ const address = creatorAddress || this.walletAddress;
18945
+ const response = await this.httpClient.get(API_ENDPOINTS.VAULTS_BY_CREATOR, {
18946
+ params: { address },
18947
+ });
18948
+ if (response.code === 200) {
18949
+ return { status: true, data: response.data || [] };
18950
+ }
18951
+ return { status: false, error: response.message || "Failed to list vaults" };
18952
+ }
18953
+ catch (error) {
18954
+ return { status: false, error: formatError(error) };
18955
+ }
18956
+ }
18498
18957
  }
18499
18958
 
18500
18959
  // Copyright (c) 2025 Dipcoin LLC
@@ -18509,33 +18968,133 @@ function initDipCoinPerpSDK(privateKey, options) {
18509
18968
  return new DipCoinPerpSDK(privateKey, sdkOptions);
18510
18969
  }
18511
18970
 
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");
18971
+ const globalEnv = path__default["default"].join(os__default["default"].homedir(), ".config", "dipcoin", "env");
18972
+ const localEnv = path__default["default"].resolve(process.cwd(), ".env");
18973
+ if (fs__default["default"].existsSync(globalEnv)) {
18974
+ dotenv__default["default"].config({ path: globalEnv });
18975
+ }
18976
+ else if (fs__default["default"].existsSync(localEnv)) {
18977
+ dotenv__default["default"].config({ path: localEnv });
18978
+ }
18979
+ // Cache SDK instances by a composite key
18980
+ const sdkCache = new Map();
18981
+ /**
18982
+ * Derive an Ed25519 keypair from a mnemonic at a given account index.
18983
+ * Uses SLIP-0010 path: m/44'/784'/{index}'/0'/0'
18984
+ */
18985
+ function deriveKeypairFromMnemonic(mnemonic, index) {
18986
+ const path = `m/44'/784'/${index}'/0'/0'`;
18987
+ return ed25519.Ed25519Keypair.deriveKeypair(mnemonic, path);
18988
+ }
18989
+ /**
18990
+ * Read and validate MNEMONIC from env.
18991
+ */
18992
+ function getMnemonic() {
18993
+ const mnemonic = process.env.MNEMONIC;
18994
+ if (!mnemonic) {
18995
+ console.error(`Error: MNEMONIC not set. Create config at ${globalEnv}`);
18520
18996
  process.exit(1);
18521
18997
  }
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;
18998
+ return mnemonic;
18529
18999
  }
18530
- function getVaultAddress(cmdVault) {
18531
- return cmdVault || process.env.VAULT_ADDRESS || undefined;
19000
+ /**
19001
+ * Get the network from env.
19002
+ */
19003
+ function getNetwork() {
19004
+ return process.env.NETWORK || "testnet";
18532
19005
  }
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);
19006
+ /**
19007
+ * Trading mode SDK: main keypair = index 0, sub keypair = index N.
19008
+ * Used for order signing where creator = main address, signer = sub keypair.
19009
+ * When no vaultIndex provided, returns SDK with main account only (index 0).
19010
+ */
19011
+ function getSDK(vaultIndex) {
19012
+ const cacheKey = `trading:${vaultIndex ?? "main"}`;
19013
+ if (sdkCache.has(cacheKey))
19014
+ return sdkCache.get(cacheKey);
19015
+ const mnemonic = getMnemonic();
19016
+ const network = getNetwork();
19017
+ const mainKeypair = deriveKeypairFromMnemonic(mnemonic, 0);
19018
+ if (vaultIndex !== undefined && vaultIndex > 0) {
19019
+ const subKeypair = deriveKeypairFromMnemonic(mnemonic, vaultIndex);
19020
+ const sdk = initDipCoinPerpSDK(mainKeypair, { network, subAccountKey: subKeypair });
19021
+ sdkCache.set(cacheKey, sdk);
19022
+ return sdk;
19023
+ }
19024
+ const sdk = initDipCoinPerpSDK(mainKeypair, { network });
19025
+ sdkCache.set(cacheKey, sdk);
19026
+ return sdk;
19027
+ }
19028
+ /**
19029
+ * Vault on-chain mode SDK: main keypair = index N.
19030
+ * Used for vault's own deposit/withdraw/balance where the vault keypair signs the tx.
19031
+ */
19032
+ function getVaultSDK(vaultIndex) {
19033
+ const cacheKey = `vault:${vaultIndex}`;
19034
+ if (sdkCache.has(cacheKey))
19035
+ return sdkCache.get(cacheKey);
19036
+ const mnemonic = getMnemonic();
19037
+ const network = getNetwork();
19038
+ const vaultKeypair = deriveKeypairFromMnemonic(mnemonic, vaultIndex);
19039
+ const sdk = initDipCoinPerpSDK(vaultKeypair, { network });
19040
+ sdkCache.set(cacheKey, sdk);
19041
+ return sdk;
19042
+ }
19043
+ /**
19044
+ * Resolve vault address from vault index or explicit address.
19045
+ * Returns undefined for main account (no vault).
19046
+ */
19047
+ function resolveVaultAddress(vaultIndex, vaultAddr) {
19048
+ if (vaultAddr)
19049
+ return vaultAddr;
19050
+ if (vaultIndex !== undefined && vaultIndex > 0) {
19051
+ const keypair = deriveKeypairFromMnemonic(getMnemonic(), vaultIndex);
19052
+ return keypair.getPublicKey().toSuiAddress();
19053
+ }
19054
+ return undefined;
19055
+ }
19056
+ /**
19057
+ * List derived vault addresses from mnemonic.
19058
+ */
19059
+ function listVaultAddresses(count) {
19060
+ const mnemonic = getMnemonic();
19061
+ const results = [];
19062
+ for (let i = 0; i < count; i++) {
19063
+ const path = `m/44'/784'/${i}'/0'/0'`;
19064
+ const keypair = deriveKeypairFromMnemonic(mnemonic, i);
19065
+ results.push({ index: i, address: keypair.getPublicKey().toSuiAddress(), path });
19066
+ }
19067
+ return results;
19068
+ }
19069
+ /**
19070
+ * Get the appropriate SDK for on-chain operations (deposit/withdraw/margin).
19071
+ * Uses the vault's own keypair when vault-index > 0, otherwise main account.
19072
+ */
19073
+ function getOnChainSDK(vaultIndex) {
19074
+ return (vaultIndex !== undefined && vaultIndex > 0) ? getVaultSDK(vaultIndex) : getSDK();
19075
+ }
19076
+
19077
+ /**
19078
+ * Get the global --vault-index value from the root program.
19079
+ */
19080
+ function getGlobalVaultIndex(prog) {
19081
+ const opts = prog.opts();
19082
+ if (opts.vaultIndex !== undefined) {
19083
+ const n = Number(opts.vaultIndex);
19084
+ if (isNaN(n) || n < 0) {
19085
+ console.error("Error: --vault-index must be a non-negative integer");
19086
+ process.exit(1);
19087
+ }
19088
+ return n;
18538
19089
  }
19090
+ // Fallback to DEFAULT_VAULT_INDEX env
19091
+ const envDefault = process.env.DEFAULT_VAULT_INDEX;
19092
+ if (envDefault !== undefined) {
19093
+ const n = Number(envDefault);
19094
+ if (!isNaN(n) && n >= 0)
19095
+ return n;
19096
+ }
19097
+ return undefined;
18539
19098
  }
18540
19099
 
18541
19100
  function isJson(cmd) {
@@ -18561,6 +19120,26 @@ function handleError(error) {
18561
19120
  console.error("Error:", typeof error === "string" ? error : error?.message || JSON.stringify(error));
18562
19121
  process.exit(1);
18563
19122
  }
19123
+ /**
19124
+ * Print a standard transaction result (success message or failure with exit).
19125
+ */
19126
+ function printTxResult(program, tx, successMsg) {
19127
+ const status = tx?.effects?.status?.status;
19128
+ const error = tx?.effects?.status?.error;
19129
+ if (isJson(program))
19130
+ return printJson({ digest: tx?.digest, status, error });
19131
+ if (status === "failure") {
19132
+ console.error(`Failed: ${error}`);
19133
+ process.exit(1);
19134
+ }
19135
+ console.log(`${successMsg} Tx: ${tx?.digest}`);
19136
+ }
19137
+ /**
19138
+ * Normalize a symbol to include "-PERP" suffix if missing.
19139
+ */
19140
+ function normalizeSymbol(s) {
19141
+ return s.includes("-") ? s : `${s}-PERP`;
19142
+ }
18564
19143
 
18565
19144
  function registerAccountCommands(program) {
18566
19145
  const account = program.command("account").description("Account operations");
@@ -18570,17 +19149,18 @@ function registerAccountCommands(program) {
18570
19149
  .option("--vault <address>", "Vault address")
18571
19150
  .action(async (opts) => {
18572
19151
  try {
18573
- const sdk = getSDK();
18574
- await ensureAuth(sdk);
18575
- const parentAddress = opts.vault || sdk.address;
19152
+ const vaultIndex = getGlobalVaultIndex(program);
19153
+ const sdk = getSDK(vaultIndex);
19154
+ const parentAddress = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
18576
19155
  const result = await sdk.getAccountInfo({ parentAddress });
18577
19156
  if (!result.status || !result.data)
18578
19157
  return handleError(result.error);
18579
19158
  if (isJson(program))
18580
19159
  return printJson(result.data);
18581
19160
  const d = result.data;
19161
+ const label = parentAddress !== sdk.address ? parentAddress : sdk.address;
18582
19162
  printTable(["Field", "Value"], [
18583
- ["Wallet", sdk.address],
19163
+ ["Wallet", label],
18584
19164
  ["Wallet Balance", formatWei(d.walletBalance)],
18585
19165
  ["Account Value", formatWei(d.accountValue)],
18586
19166
  ["Free Collateral", formatWei(d.freeCollateral)],
@@ -18598,18 +19178,10 @@ function registerAccountCommands(program) {
18598
19178
  .argument("<amount>", "Amount in USDC")
18599
19179
  .action(async (amount) => {
18600
19180
  try {
18601
- const sdk = getSDK();
19181
+ const vaultIndex = getGlobalVaultIndex(program);
19182
+ const sdk = getOnChainSDK(vaultIndex);
18602
19183
  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}`);
19184
+ printTxResult(program, tx, `Deposit ${amount} USDC succeeded.`);
18613
19185
  }
18614
19186
  catch (e) {
18615
19187
  handleError(e);
@@ -18621,18 +19193,10 @@ function registerAccountCommands(program) {
18621
19193
  .argument("<amount>", "Amount in USDC")
18622
19194
  .action(async (amount) => {
18623
19195
  try {
18624
- const sdk = getSDK();
19196
+ const vaultIndex = getGlobalVaultIndex(program);
19197
+ const sdk = getOnChainSDK(vaultIndex);
18625
19198
  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}`);
19199
+ printTxResult(program, tx, `Withdraw ${amount} USDC succeeded.`);
18636
19200
  }
18637
19201
  catch (e) {
18638
19202
  handleError(e);
@@ -18640,18 +19204,41 @@ function registerAccountCommands(program) {
18640
19204
  });
18641
19205
  }
18642
19206
 
18643
- async function placeOrder(program, side, symbol, quantity, opts) {
19207
+ /**
19208
+ * Parse amount string: "100USDC" -> 100, "100" -> 100
19209
+ */
19210
+ function parseUsdcAmount(raw) {
19211
+ const cleaned = raw.replace(/usdc$/i, "");
19212
+ const val = parseFloat(cleaned);
19213
+ if (isNaN(val) || val <= 0)
19214
+ throw new Error(`Invalid amount: ${raw}`);
19215
+ return val;
19216
+ }
19217
+ /**
19218
+ * Parse leverage string: "10x" -> "10", "10" -> "10"
19219
+ */
19220
+ function parseLeverage(raw) {
19221
+ const cleaned = raw.replace(/x$/i, "");
19222
+ const val = parseFloat(cleaned);
19223
+ if (isNaN(val) || val <= 0)
19224
+ throw new Error(`Invalid leverage: ${raw}`);
19225
+ return cleaned;
19226
+ }
19227
+ async function placeOrder(program, side, symbol, amount, leverage, opts) {
18644
19228
  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) {
19229
+ const vaultIndex = getGlobalVaultIndex(program);
19230
+ const sdk = getSDK(vaultIndex);
19231
+ const lev = parseLeverage(leverage);
19232
+ const orderType = opts.price ? OrderType.LIMIT : OrderType.MARKET;
19233
+ symbol = normalizeSymbol(symbol);
19234
+ let quantity;
19235
+ if (opts.qty) {
19236
+ // Explicit quantity mode
19237
+ quantity = opts.qty;
19238
+ }
19239
+ else {
19240
+ // Default: USDC margin mode
19241
+ const usdcAmount = parseUsdcAmount(amount);
18655
19242
  const tickerResult = await sdk.getTicker(symbol);
18656
19243
  if (!tickerResult.status || !tickerResult.data)
18657
19244
  return handleError("Failed to get market price for USDC conversion");
@@ -18659,9 +19246,7 @@ async function placeOrder(program, side, symbol, quantity, opts) {
18659
19246
  if (!priceWei || priceWei <= 0)
18660
19247
  return handleError("Invalid market price");
18661
19248
  const price = priceWei / 1e18;
18662
- const lev = parseFloat(leverage);
18663
- const usdcAmount = parseFloat(opts.usdc);
18664
- const rawQty = usdcAmount * lev / price;
19249
+ const rawQty = usdcAmount * parseFloat(lev) / price;
18665
19250
  let stepSize = 0.01;
18666
19251
  const pairResult = await sdk.getTradingPairs();
18667
19252
  if (pairResult.status && pairResult.data) {
@@ -18682,7 +19267,7 @@ async function placeOrder(program, side, symbol, quantity, opts) {
18682
19267
  quantity = stepped.toFixed(decimals);
18683
19268
  if (parseFloat(quantity) <= 0)
18684
19269
  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})`);
19270
+ console.log(`USDC ${usdcAmount} -> quantity ${quantity} (price: ${price}, leverage: ${lev}x, step: ${stepSize})`);
18686
19271
  }
18687
19272
  const perpId = await sdk.getPerpetualID(symbol);
18688
19273
  if (!perpId)
@@ -18693,16 +19278,22 @@ async function placeOrder(program, side, symbol, quantity, opts) {
18693
19278
  side,
18694
19279
  orderType,
18695
19280
  quantity,
18696
- leverage,
19281
+ leverage: lev,
18697
19282
  reduceOnly: opts.reduceOnly || false,
18698
19283
  };
18699
- if (orderType === OrderType.LIMIT) {
18700
- if (!opts.price)
18701
- return handleError("--price is required for LIMIT orders");
19284
+ if (opts.price) {
18702
19285
  params.price = opts.price;
18703
19286
  }
18704
- if (vault)
18705
- params.creator = vault;
19287
+ // --vault <address> as explicit creator override (fallback)
19288
+ if (opts.vault) {
19289
+ params.creator = opts.vault;
19290
+ }
19291
+ else {
19292
+ // With vault-index, the SDK handles creator via sub-keypair automatically
19293
+ const vaultAddr = resolveVaultAddress(vaultIndex);
19294
+ if (vaultAddr)
19295
+ params.creator = vaultAddr;
19296
+ }
18706
19297
  if (opts.tp)
18707
19298
  params.tpTriggerPrice = opts.tp;
18708
19299
  if (opts.sl)
@@ -18723,24 +19314,24 @@ async function placeOrder(program, side, symbol, quantity, opts) {
18723
19314
  function registerTradeCommands(program) {
18724
19315
  const trade = program.command("trade").description("Trading operations");
18725
19316
  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")
19317
+ .option("--qty <quantity>", "Specify order quantity instead of USDC margin")
19318
+ .option("--price <p>", "Limit order price (auto-enables limit order)")
18729
19319
  .option("--reduce-only", "Reduce only order")
18730
19320
  .option("--tp <price>", "Take profit trigger price")
18731
19321
  .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)");
19322
+ .option("--vault <address>", "Vault/creator address (fallback)");
18734
19323
  orderOpts(trade
18735
19324
  .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));
19325
+ .description("Place a BUY order (e.g. trade buy BTC 100USDC 10x)")
19326
+ .argument("<symbol>", "Trading pair (e.g. BTC or BTC-PERP)")
19327
+ .argument("<amount>", "USDC margin amount (e.g. 100 or 100USDC)")
19328
+ .argument("<leverage>", "Leverage multiplier (e.g. 10 or 10x)")).action((symbol, amount, leverage, opts) => placeOrder(program, OrderSide.BUY, symbol, amount, leverage, opts));
18739
19329
  orderOpts(trade
18740
19330
  .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));
19331
+ .description("Place a SELL order (e.g. trade sell BTC 100USDC 10x)")
19332
+ .argument("<symbol>", "Trading pair (e.g. BTC or BTC-PERP)")
19333
+ .argument("<amount>", "USDC margin amount (e.g. 100 or 100USDC)")
19334
+ .argument("<leverage>", "Leverage multiplier (e.g. 10 or 10x)")).action((symbol, amount, leverage, opts) => placeOrder(program, OrderSide.SELL, symbol, amount, leverage, opts));
18744
19335
  trade
18745
19336
  .command("cancel")
18746
19337
  .description("Cancel orders by hash")
@@ -18749,13 +19340,14 @@ function registerTradeCommands(program) {
18749
19340
  .option("--vault <address>", "Parent address")
18750
19341
  .action(async (symbol, hashes, opts) => {
18751
19342
  try {
18752
- const sdk = getSDK();
18753
- await ensureAuth(sdk);
18754
- const vault = opts.vault || sdk.address;
19343
+ symbol = normalizeSymbol(symbol);
19344
+ const vaultIndex = getGlobalVaultIndex(program);
19345
+ const sdk = getSDK(vaultIndex);
19346
+ const parentAddress = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
18755
19347
  const result = await sdk.cancelOrder({
18756
19348
  symbol,
18757
19349
  orderHashes: hashes,
18758
- parentAddress: vault,
19350
+ parentAddress,
18759
19351
  });
18760
19352
  if (!result.status)
18761
19353
  return handleError(result.error);
@@ -18778,9 +19370,9 @@ function registerPositionCommands(program) {
18778
19370
  .option("--vault <address>", "Vault address")
18779
19371
  .action(async (opts) => {
18780
19372
  try {
18781
- const sdk = getSDK();
18782
- await ensureAuth(sdk);
18783
- const vault = opts.vault || sdk.address;
19373
+ const vaultIndex = getGlobalVaultIndex(program);
19374
+ const sdk = getSDK(vaultIndex);
19375
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
18784
19376
  const params = {
18785
19377
  parentAddress: vault,
18786
19378
  ...(opts.symbol ? { symbol: opts.symbol } : {}),
@@ -18823,7 +19415,7 @@ function registerPositionCommands(program) {
18823
19415
  .action(async (symbol, opts) => {
18824
19416
  try {
18825
19417
  const sdk = getSDK();
18826
- await ensureAuth(sdk);
19418
+ symbol = normalizeSymbol(symbol);
18827
19419
  const perpId = await sdk.getPerpetualID(symbol);
18828
19420
  if (!perpId)
18829
19421
  return handleError(`PerpetualID not found for ${symbol}`);
@@ -18872,7 +19464,8 @@ function registerPositionCommands(program) {
18872
19464
  .argument("<amount>", "Amount in USDC")
18873
19465
  .action(async (symbol, amount) => {
18874
19466
  try {
18875
- const sdk = getSDK();
19467
+ const vaultIndex = getGlobalVaultIndex(program);
19468
+ const sdk = getOnChainSDK(vaultIndex);
18876
19469
  const tx = await sdk.addMargin({ symbol, amount: Number(amount) });
18877
19470
  if (isJson(program))
18878
19471
  return printJson({ digest: tx?.digest, status: "ok" });
@@ -18889,7 +19482,8 @@ function registerPositionCommands(program) {
18889
19482
  .argument("<amount>", "Amount in USDC")
18890
19483
  .action(async (symbol, amount) => {
18891
19484
  try {
18892
- const sdk = getSDK();
19485
+ const vaultIndex = getGlobalVaultIndex(program);
19486
+ const sdk = getOnChainSDK(vaultIndex);
18893
19487
  const tx = await sdk.removeMargin({ symbol, amount: Number(amount) });
18894
19488
  if (isJson(program))
18895
19489
  return printJson({ digest: tx?.digest, status: "ok" });
@@ -18926,6 +19520,7 @@ function registerMarketCommands(program) {
18926
19520
  .argument("<symbol>", "Trading pair")
18927
19521
  .action(async (symbol) => {
18928
19522
  try {
19523
+ symbol = normalizeSymbol(symbol);
18929
19524
  const sdk = getSDK();
18930
19525
  const result = await sdk.getTicker(symbol);
18931
19526
  if (!result.status || !result.data)
@@ -18954,6 +19549,7 @@ function registerMarketCommands(program) {
18954
19549
  .argument("<symbol>", "Trading pair")
18955
19550
  .action(async (symbol) => {
18956
19551
  try {
19552
+ symbol = normalizeSymbol(symbol);
18957
19553
  const sdk = getSDK();
18958
19554
  const result = await sdk.getOrderBook(symbol);
18959
19555
  if (!result.status || !result.data)
@@ -18981,6 +19577,7 @@ function registerMarketCommands(program) {
18981
19577
  .argument("<symbol>", "Trading pair")
18982
19578
  .action(async (symbol) => {
18983
19579
  try {
19580
+ symbol = normalizeSymbol(symbol);
18984
19581
  const sdk = getSDK();
18985
19582
  const result = await sdk.getOraclePrice(symbol);
18986
19583
  if (!result.status)
@@ -19004,16 +19601,20 @@ function registerHistoryCommands(program) {
19004
19601
  .option("--page <n>", "Page number", "1")
19005
19602
  .option("--size <n>", "Page size", "20")
19006
19603
  .option("--vault <address>", "Vault address")
19604
+ .option("--begin-time <ms>", "Begin time filter (epoch ms)")
19605
+ .option("--end-time <ms>", "End time filter (epoch ms)")
19007
19606
  .action(async (opts) => {
19008
19607
  try {
19009
- const sdk = getSDK();
19010
- await ensureAuth(sdk);
19011
- const vault = opts.vault || sdk.address;
19608
+ const vaultIndex = getGlobalVaultIndex(program);
19609
+ const sdk = getSDK(vaultIndex);
19610
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
19012
19611
  const result = await sdk.getHistoryOrders({
19013
19612
  ...(opts.symbol ? { symbol: opts.symbol } : {}),
19014
19613
  page: Number(opts.page),
19015
19614
  pageSize: Number(opts.size),
19016
19615
  parentAddress: vault,
19616
+ ...(opts.beginTime ? { beginTime: Number(opts.beginTime) } : {}),
19617
+ ...(opts.endTime ? { endTime: Number(opts.endTime) } : {}),
19017
19618
  });
19018
19619
  if (!result.status)
19019
19620
  return handleError(result.error);
@@ -19043,16 +19644,18 @@ function registerHistoryCommands(program) {
19043
19644
  .option("--page <n>", "Page number", "1")
19044
19645
  .option("--size <n>", "Page size", "20")
19045
19646
  .option("--vault <address>", "Vault address")
19647
+ .option("--begin-time <ms>", "Begin time filter (epoch ms)")
19046
19648
  .action(async (opts) => {
19047
19649
  try {
19048
- const sdk = getSDK();
19049
- await ensureAuth(sdk);
19050
- const vault = opts.vault || sdk.address;
19650
+ const vaultIndex = getGlobalVaultIndex(program);
19651
+ const sdk = getSDK(vaultIndex);
19652
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
19051
19653
  const result = await sdk.getFundingSettlements({
19052
19654
  ...(opts.symbol ? { symbol: opts.symbol } : {}),
19053
19655
  page: Number(opts.page),
19054
19656
  pageSize: Number(opts.size),
19055
19657
  parentAddress: vault,
19658
+ ...(opts.beginTime ? { beginTime: Number(opts.beginTime) } : {}),
19056
19659
  });
19057
19660
  if (!result.status)
19058
19661
  return handleError(result.error);
@@ -19060,12 +19663,13 @@ function registerHistoryCommands(program) {
19060
19663
  return printJson(result.data);
19061
19664
  if (!result.data?.data?.length)
19062
19665
  return console.log("No funding settlements.");
19063
- printTable(["Symbol", "Rate", "Fee", "Qty", "Side", "Time"], result.data.data.map((f) => [
19666
+ printTable(["Symbol", "Side", "Size", "Rate", "Settlement", "Price", "Time"], result.data.data.map((f) => [
19064
19667
  f.symbol || "-",
19668
+ f.positionIsLong === 1 ? "LONG" : f.positionIsLong === 0 ? "SHORT" : (f.side || "-"),
19669
+ formatWei(f.size || f.quantity),
19065
19670
  formatWei(f.fundingRate),
19066
- formatWei(f.fundingFee),
19067
- formatWei(f.quantity),
19068
- f.side || "-",
19671
+ formatWei(f.settlementAmount || f.fundingFee),
19672
+ formatWei(f.oraclePrice),
19069
19673
  f.createdAt ? new Date(f.createdAt).toLocaleString() : "-",
19070
19674
  ]));
19071
19675
  }
@@ -19079,15 +19683,17 @@ function registerHistoryCommands(program) {
19079
19683
  .option("--page <n>", "Page number", "1")
19080
19684
  .option("--size <n>", "Page size", "20")
19081
19685
  .option("--vault <address>", "Vault address")
19686
+ .option("--begin-time <ms>", "Begin time filter (epoch ms)")
19082
19687
  .action(async (opts) => {
19083
19688
  try {
19084
- const sdk = getSDK();
19085
- await ensureAuth(sdk);
19086
- const vault = opts.vault || sdk.address;
19689
+ const vaultIndex = getGlobalVaultIndex(program);
19690
+ const sdk = getSDK(vaultIndex);
19691
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
19087
19692
  const result = await sdk.getBalanceChanges({
19088
19693
  page: Number(opts.page),
19089
19694
  pageSize: Number(opts.size),
19090
19695
  parentAddress: vault,
19696
+ ...(opts.beginTime ? { beginTime: Number(opts.beginTime) } : {}),
19091
19697
  });
19092
19698
  if (!result.status)
19093
19699
  return handleError(result.error);
@@ -19095,12 +19701,11 @@ function registerHistoryCommands(program) {
19095
19701
  return printJson(result.data);
19096
19702
  if (!result.data?.data?.length)
19097
19703
  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() : "-",
19704
+ printTable(["Type", "Amount", "TxDigest", "Time"], result.data.data.map((b) => [
19705
+ b.bizTypeDesc || b.type || "-",
19706
+ formatWei(b.settlementAmount || b.amount),
19707
+ b.txDigest ? b.txDigest.slice(0, 16) + "..." : "-",
19708
+ (b.createdTime || b.createdAt) ? new Date(b.createdTime || b.createdAt).toLocaleString() : "-",
19104
19709
  ]));
19105
19710
  }
19106
19711
  catch (e) {
@@ -19109,55 +19714,338 @@ function registerHistoryCommands(program) {
19109
19714
  });
19110
19715
  }
19111
19716
 
19717
+ const USDC_DECIMALS = 6;
19718
+ /**
19719
+ * Format a USDC atomic value (6 decimals) to human-readable.
19720
+ */
19721
+ function formatUsdc(value) {
19722
+ if (value === undefined || value === null || value === "")
19723
+ return "0";
19724
+ const bn = new BigNumber__default["default"](String(value));
19725
+ if (bn.isNaN())
19726
+ return "0";
19727
+ return bn.dividedBy(new BigNumber__default["default"](10).pow(USDC_DECIMALS)).toFixed(USDC_DECIMALS);
19728
+ }
19729
+ /**
19730
+ * Format a ratio (9-decimal, where 10^9 = 100%) to percentage string.
19731
+ */
19732
+ function formatRatio(value) {
19733
+ if (value === undefined || value === null || value === "")
19734
+ return "0%";
19735
+ const bn = new BigNumber__default["default"](String(value));
19736
+ if (bn.isNaN())
19737
+ return "0%";
19738
+ return bn.dividedBy(new BigNumber__default["default"](10).pow(7)).toFixed(2) + "%";
19739
+ }
19740
+ /**
19741
+ * Convert percentage to 9-decimal ratio string.
19742
+ * E.g., "10" (10%) -> "100000000" (10 * 10^7)
19743
+ */
19744
+ function percentToRatio(pct) {
19745
+ const val = parseFloat(pct);
19746
+ if (isNaN(val) || val < 0)
19747
+ throw new Error(`Invalid percentage: ${pct}`);
19748
+ return new BigNumber__default["default"](val).multipliedBy(new BigNumber__default["default"](10).pow(7)).toFixed(0);
19749
+ }
19112
19750
  function registerVaultCommands(program) {
19113
- const vault = program.command("vault").description("Vault / sub-account operations");
19751
+ const vault = program.command("vault").description("On-chain vault operations");
19752
+ // === vault create ===
19114
19753
  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) => {
19754
+ .command("create")
19755
+ .description("Create a new on-chain vault")
19756
+ .requiredOption("--name <name>", "Vault display name")
19757
+ .requiredOption("--trader <address>", "Trader address")
19758
+ .requiredOption("--max-cap <usdc>", "Maximum USDC cap")
19759
+ .requiredOption("--min-deposit <usdc>", "Minimum deposit amount in USDC")
19760
+ .requiredOption("--creator-share <pct>", "Creator minimum share ratio (%)")
19761
+ .requiredOption("--profit-share <pct>", "Creator profit share ratio (%)")
19762
+ .requiredOption("--initial <usdc>", "Initial deposit in USDC")
19763
+ .action(async (opts) => {
19119
19764
  try {
19120
19765
  const sdk = getSDK();
19121
- const tx = await sdk.setSubAccount(subAddress);
19766
+ console.log(`Creating vault "${opts.name}"...`);
19767
+ const tx = await sdk.createVault({
19768
+ name: opts.name,
19769
+ trader: opts.trader,
19770
+ maxCap: Number(opts.maxCap),
19771
+ minDepositAmount: Number(opts.minDeposit),
19772
+ creatorMinimumShareRatio: percentToRatio(opts.creatorShare),
19773
+ creatorProfitShareRatio: percentToRatio(opts.profitShare),
19774
+ initialAmount: Number(opts.initial),
19775
+ });
19776
+ printTxResult(program, tx, `Vault "${opts.name}" created.`);
19777
+ }
19778
+ catch (e) {
19779
+ handleError(e);
19780
+ }
19781
+ });
19782
+ // === vault list ===
19783
+ vault
19784
+ .command("list")
19785
+ .description("List vaults created by current wallet")
19786
+ .action(async () => {
19787
+ try {
19788
+ const sdk = getSDK();
19789
+ const result = await sdk.listVaults();
19790
+ if (!result.status)
19791
+ return handleError(result.error);
19122
19792
  if (isJson(program))
19123
- return printJson({ digest: tx?.digest, status: "ok" });
19124
- console.log(`Sub-account ${subAddress} set. Tx: ${tx?.digest || JSON.stringify(tx)}`);
19793
+ return printJson(result.data);
19794
+ if (!result.data?.length)
19795
+ return console.log("No vaults found.");
19796
+ printTable(["Vault ID", "Name", "Trader", "Deposit", "Status"], result.data.map((v) => [
19797
+ v.vaultId,
19798
+ v.name || "-",
19799
+ v.trader,
19800
+ v.depositStatus === 1 ? "Open" : "Closed",
19801
+ v.closedAt > 0 ? "Closed" : "Active",
19802
+ ]));
19125
19803
  }
19126
19804
  catch (e) {
19127
19805
  handleError(e);
19128
19806
  }
19129
19807
  });
19808
+ // === vault info ===
19130
19809
  vault
19131
19810
  .command("info")
19132
- .description("Show vault account info")
19133
- .option("--address <address>", "Vault address (or use VAULT_ADDRESS env)")
19134
- .action(async (opts) => {
19811
+ .description("Show vault details (e.g. vault info <vaultId>)")
19812
+ .argument("<vaultId>", "Vault object ID")
19813
+ .action(async (vaultId) => {
19135
19814
  try {
19136
19815
  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 });
19816
+ const result = await sdk.getVaultInfo(vaultId);
19142
19817
  if (!result.status || !result.data)
19143
19818
  return handleError(result.error);
19144
19819
  if (isJson(program))
19145
19820
  return printJson(result.data);
19146
19821
  const d = result.data;
19147
19822
  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)],
19823
+ ["Vault ID", vaultId],
19824
+ ["Name", d.name || "-"],
19825
+ ["Creator", d.creator || "-"],
19826
+ ["Trader", d.trader || "-"],
19827
+ ["Deposit Status", d.deposit_status === true || d.deposit_status === "true" ? "Open" : "Closed"],
19828
+ ["Total Shares", formatUsdc(d.total_shares)],
19829
+ ["Max Cap", formatUsdc(d.max_cap)],
19830
+ ["Min Deposit", formatUsdc(d.min_deposit_amount)],
19831
+ ["Creator Min Share Ratio", formatRatio(d.creator_minimum_share_ratio)],
19832
+ ["Creator Profit Share Ratio", formatRatio(d.creator_profit_share_ratio)],
19833
+ ["Auto Close on Withdraw", String(d.auto_close_on_withdraw ?? "-")],
19154
19834
  ]);
19155
19835
  }
19156
19836
  catch (e) {
19157
19837
  handleError(e);
19158
19838
  }
19159
19839
  });
19840
+ // === vault deposit ===
19841
+ vault
19842
+ .command("deposit")
19843
+ .description("Deposit USDC into a vault (e.g. vault deposit <vaultId> <amount>)")
19844
+ .argument("<vaultId>", "Vault object ID")
19845
+ .argument("<amount>", "USDC amount to deposit")
19846
+ .action(async (vaultId, amount) => {
19847
+ try {
19848
+ const sdk = getSDK();
19849
+ console.log(`Depositing ${amount} USDC to vault ${vaultId}...`);
19850
+ const tx = await sdk.depositToVault({ vaultID: vaultId, amount: Number(amount) });
19851
+ printTxResult(program, tx, `Deposited ${amount} USDC.`);
19852
+ }
19853
+ catch (e) {
19854
+ handleError(e);
19855
+ }
19856
+ });
19857
+ // === vault withdraw ===
19858
+ vault
19859
+ .command("withdraw")
19860
+ .description("Request withdrawal from vault (e.g. vault withdraw <vaultId> <shares>)")
19861
+ .argument("<vaultId>", "Vault object ID")
19862
+ .argument("<shares>", "Number of shares to withdraw")
19863
+ .action(async (vaultId, shares) => {
19864
+ try {
19865
+ const sdk = getSDK();
19866
+ console.log(`Requesting withdrawal of ${shares} shares from vault ${vaultId}...`);
19867
+ const tx = await sdk.requestWithdrawFromVault({ vaultID: vaultId, shares: Number(shares) });
19868
+ printTxResult(program, tx, `Withdrawal request submitted for ${shares} shares.`);
19869
+ }
19870
+ catch (e) {
19871
+ handleError(e);
19872
+ }
19873
+ });
19874
+ // === vault fill ===
19875
+ vault
19876
+ .command("fill")
19877
+ .description("Fill pending withdrawal requests (e.g. vault fill <vaultId> <requestIDs...>)")
19878
+ .argument("<vaultId>", "Vault object ID")
19879
+ .argument("<requestIDs...>", "Withdrawal request object IDs")
19880
+ .option("--markets <ids>", "Comma-separated market PerpetualIDs for share price update")
19881
+ .action(async (vaultId, requestIDs, opts) => {
19882
+ try {
19883
+ const sdk = getSDK();
19884
+ const markets = opts.markets ? opts.markets.split(",") : undefined;
19885
+ console.log(`Filling ${requestIDs.length} withdrawal request(s)...`);
19886
+ const tx = await sdk.fillWithdrawalRequests({
19887
+ vaultID: vaultId,
19888
+ withdrawalRequestIDs: requestIDs,
19889
+ markets,
19890
+ });
19891
+ printTxResult(program, tx, `Filled ${requestIDs.length} withdrawal request(s).`);
19892
+ }
19893
+ catch (e) {
19894
+ handleError(e);
19895
+ }
19896
+ });
19897
+ // === vault close ===
19898
+ vault
19899
+ .command("close")
19900
+ .description("Close a vault (e.g. vault close <vaultId>)")
19901
+ .argument("<vaultId>", "Vault object ID")
19902
+ .option("--markets <ids>", "Comma-separated market PerpetualIDs")
19903
+ .action(async (vaultId, opts) => {
19904
+ try {
19905
+ const sdk = getSDK();
19906
+ const markets = opts.markets ? opts.markets.split(",") : undefined;
19907
+ console.log(`Closing vault ${vaultId}...`);
19908
+ const tx = await sdk.closeVault({ vaultID: vaultId, markets });
19909
+ printTxResult(program, tx, "Vault closed.");
19910
+ }
19911
+ catch (e) {
19912
+ handleError(e);
19913
+ }
19914
+ });
19915
+ // === vault remove ===
19916
+ vault
19917
+ .command("remove")
19918
+ .description("Remove a closed vault (e.g. vault remove <vaultId>)")
19919
+ .argument("<vaultId>", "Vault object ID")
19920
+ .action(async (vaultId) => {
19921
+ try {
19922
+ const sdk = getSDK();
19923
+ console.log(`Removing vault ${vaultId}...`);
19924
+ const tx = await sdk.removeVault({ vaultID: vaultId });
19925
+ printTxResult(program, tx, "Vault removed.");
19926
+ }
19927
+ catch (e) {
19928
+ handleError(e);
19929
+ }
19930
+ });
19931
+ // === vault claim ===
19932
+ vault
19933
+ .command("claim")
19934
+ .description("Claim funds from a closed vault (e.g. vault claim <vaultId>)")
19935
+ .argument("<vaultId>", "Vault object ID")
19936
+ .action(async (vaultId) => {
19937
+ try {
19938
+ const sdk = getSDK();
19939
+ console.log(`Claiming funds from vault ${vaultId}...`);
19940
+ const tx = await sdk.claimClosedVaultFunds({ vaultID: vaultId });
19941
+ printTxResult(program, tx, "Funds claimed.");
19942
+ }
19943
+ catch (e) {
19944
+ handleError(e);
19945
+ }
19946
+ });
19947
+ // === vault set-trader ===
19948
+ vault
19949
+ .command("set-trader")
19950
+ .description("Change the trader address (e.g. vault set-trader <vaultId> <address>)")
19951
+ .argument("<vaultId>", "Vault object ID")
19952
+ .argument("<address>", "New trader address")
19953
+ .action(async (vaultId, address) => {
19954
+ try {
19955
+ const sdk = getSDK();
19956
+ const tx = await sdk.setVaultTrader({ vaultID: vaultId, newTrader: address });
19957
+ printTxResult(program, tx, `Trader set to ${address}.`);
19958
+ }
19959
+ catch (e) {
19960
+ handleError(e);
19961
+ }
19962
+ });
19963
+ // === vault set-sub-trader ===
19964
+ vault
19965
+ .command("set-sub-trader")
19966
+ .description("Add or remove a sub-trader (e.g. vault set-sub-trader <vaultId> <address>)")
19967
+ .argument("<vaultId>", "Vault object ID")
19968
+ .argument("<address>", "Sub-trader address")
19969
+ .option("--disable", "Remove sub-trader (default: add)")
19970
+ .action(async (vaultId, address, opts) => {
19971
+ try {
19972
+ const sdk = getSDK();
19973
+ const status = !opts.disable;
19974
+ const tx = await sdk.setVaultSubTrader({ vaultID: vaultId, subTrader: address, status });
19975
+ printTxResult(program, tx, `Sub-trader ${address} ${status ? "added" : "removed"}.`);
19976
+ }
19977
+ catch (e) {
19978
+ handleError(e);
19979
+ }
19980
+ });
19981
+ // === vault set-deposit-status ===
19982
+ vault
19983
+ .command("set-deposit-status")
19984
+ .description("Enable or disable deposits (e.g. vault set-deposit-status <vaultId>)")
19985
+ .argument("<vaultId>", "Vault object ID")
19986
+ .option("--disable", "Disable deposits (default: enable)")
19987
+ .action(async (vaultId, opts) => {
19988
+ try {
19989
+ const sdk = getSDK();
19990
+ const status = !opts.disable;
19991
+ const tx = await sdk.setVaultDepositStatus({ vaultID: vaultId, status });
19992
+ printTxResult(program, tx, `Deposits ${status ? "enabled" : "disabled"}.`);
19993
+ }
19994
+ catch (e) {
19995
+ handleError(e);
19996
+ }
19997
+ });
19998
+ // === vault set-max-cap ===
19999
+ vault
20000
+ .command("set-max-cap")
20001
+ .description("Set maximum USDC cap (e.g. vault set-max-cap <vaultId> <amount>)")
20002
+ .argument("<vaultId>", "Vault object ID")
20003
+ .argument("<amount>", "Max cap in USDC")
20004
+ .action(async (vaultId, amount) => {
20005
+ try {
20006
+ const sdk = getSDK();
20007
+ const tx = await sdk.setVaultMaxCap({ vaultID: vaultId, maxCap: Number(amount) });
20008
+ printTxResult(program, tx, `Max cap set to ${amount} USDC.`);
20009
+ }
20010
+ catch (e) {
20011
+ handleError(e);
20012
+ }
20013
+ });
20014
+ // === vault set-min-deposit ===
20015
+ vault
20016
+ .command("set-min-deposit")
20017
+ .description("Set minimum deposit amount (e.g. vault set-min-deposit <vaultId> <amount>)")
20018
+ .argument("<vaultId>", "Vault object ID")
20019
+ .argument("<amount>", "Min deposit in USDC")
20020
+ .action(async (vaultId, amount) => {
20021
+ try {
20022
+ const sdk = getSDK();
20023
+ const tx = await sdk.setVaultMinDepositAmount({ vaultID: vaultId, minDepositAmount: Number(amount) });
20024
+ printTxResult(program, tx, `Min deposit set to ${amount} USDC.`);
20025
+ }
20026
+ catch (e) {
20027
+ handleError(e);
20028
+ }
20029
+ });
20030
+ // === vault set-auto-close ===
20031
+ vault
20032
+ .command("set-auto-close")
20033
+ .description("Enable or disable auto-close on withdrawal (e.g. vault set-auto-close <vaultId>)")
20034
+ .argument("<vaultId>", "Vault object ID")
20035
+ .option("--disable", "Disable auto-close (default: enable)")
20036
+ .action(async (vaultId, opts) => {
20037
+ try {
20038
+ const sdk = getSDK();
20039
+ const enabled = !opts.disable;
20040
+ const tx = await sdk.setVaultAutoCloseOnWithdraw({ vaultID: vaultId, autoCloseOnWithdraw: enabled });
20041
+ printTxResult(program, tx, `Auto-close on withdraw ${enabled ? "enabled" : "disabled"}.`);
20042
+ }
20043
+ catch (e) {
20044
+ handleError(e);
20045
+ }
20046
+ });
19160
20047
  }
20048
+
19161
20049
  function registerOrdersCommand(program) {
19162
20050
  program
19163
20051
  .command("orders")
@@ -19166,9 +20054,9 @@ function registerOrdersCommand(program) {
19166
20054
  .option("--vault <address>", "Vault address")
19167
20055
  .action(async (opts) => {
19168
20056
  try {
19169
- const sdk = getSDK();
19170
- await ensureAuth(sdk);
19171
- const vault = opts.vault || sdk.address;
20057
+ const vaultIndex = getGlobalVaultIndex(program);
20058
+ const sdk = getSDK(vaultIndex);
20059
+ const vault = opts.vault || resolveVaultAddress(vaultIndex) || sdk.address;
19172
20060
  const params = {
19173
20061
  parentAddress: vault,
19174
20062
  ...(opts.symbol ? { symbol: opts.symbol } : {}),
@@ -19197,6 +20085,71 @@ function registerOrdersCommand(program) {
19197
20085
  });
19198
20086
  }
19199
20087
 
20088
+ function registerSubAccountCommands(program) {
20089
+ const sub = program.command("sub-account").description("HD-derived sub-account operations");
20090
+ sub
20091
+ .command("set")
20092
+ .description("Set sub-account on-chain (authorize a sub-account address)")
20093
+ .argument("<subAddress>", "Sub-account address to authorize")
20094
+ .action(async (subAddress) => {
20095
+ try {
20096
+ const sdk = getSDK();
20097
+ const tx = await sdk.setSubAccount(subAddress);
20098
+ if (isJson(program))
20099
+ return printJson({ digest: tx?.digest, status: "ok" });
20100
+ console.log(`Sub-account ${subAddress} set. Tx: ${tx?.digest || JSON.stringify(tx)}`);
20101
+ }
20102
+ catch (e) {
20103
+ handleError(e);
20104
+ }
20105
+ });
20106
+ sub
20107
+ .command("setup")
20108
+ .description("Derive sub-account at index and register it on-chain via main account")
20109
+ .argument("<index>", "Vault index to setup (1+)")
20110
+ .action(async (indexStr) => {
20111
+ try {
20112
+ const index = Number(indexStr);
20113
+ if (isNaN(index) || index < 1)
20114
+ return handleError("Vault index must be >= 1");
20115
+ const subAddress = resolveVaultAddress(index);
20116
+ if (!subAddress)
20117
+ return handleError("Could not derive vault address. Is MNEMONIC set?");
20118
+ const sdk = getSDK();
20119
+ console.log(`Registering sub-account ${index} (${subAddress})...`);
20120
+ const tx = await sdk.setSubAccount(subAddress);
20121
+ if (isJson(program))
20122
+ return printJson({ digest: tx?.digest, index, address: subAddress });
20123
+ console.log(`Sub-account ${index} setup complete. Address: ${subAddress}`);
20124
+ console.log(`Tx: ${tx?.digest || JSON.stringify(tx)}`);
20125
+ }
20126
+ catch (e) {
20127
+ handleError(e);
20128
+ }
20129
+ });
20130
+ sub
20131
+ .command("list")
20132
+ .description("List HD-derived sub-account addresses")
20133
+ .option("--count <n>", "Number of addresses to derive", "5")
20134
+ .action(async (opts) => {
20135
+ try {
20136
+ const count = Number(opts.count) || 5;
20137
+ const addresses = listVaultAddresses(count);
20138
+ if (isJson(program))
20139
+ return printJson(addresses);
20140
+ printTable(["Index", "Address", "Path", "Role"], addresses.map((a) => [
20141
+ String(a.index),
20142
+ a.address,
20143
+ a.path,
20144
+ a.index === 0 ? "Main Account" : `Sub-Account ${a.index}`,
20145
+ ]));
20146
+ }
20147
+ catch (e) {
20148
+ handleError(e);
20149
+ }
20150
+ });
20151
+ }
20152
+
19200
20153
  function formatCoinBalance(totalBalance, decimals) {
19201
20154
  const bn = new BigNumber__default["default"](totalBalance);
19202
20155
  if (bn.isNaN() || bn.isZero())
@@ -19209,14 +20162,18 @@ function registerBalanceCommand(program) {
19209
20162
  .description("Show on-chain coin balances for the current wallet")
19210
20163
  .action(async () => {
19211
20164
  try {
19212
- const sdk = getSDK();
19213
- const result = await sdk.getAllBalances();
20165
+ const vaultIndex = getGlobalVaultIndex(program);
20166
+ const sdk = getSDK(vaultIndex);
20167
+ const vaultAddr = resolveVaultAddress(vaultIndex);
20168
+ const result = await sdk.getAllBalances(vaultAddr);
19214
20169
  if (!result.status || !result.data)
19215
20170
  return handleError(result.error);
19216
20171
  if (isJson(program))
19217
20172
  return printJson(result.data);
19218
20173
  if (!result.data.length)
19219
20174
  return console.log("No coins found.");
20175
+ if (vaultAddr)
20176
+ console.log(`Balances for vault: ${vaultAddr}`);
19220
20177
  const rows = [];
19221
20178
  for (const b of result.data) {
19222
20179
  const meta = await sdk.getCoinMetadata(b.coinType);
@@ -19234,19 +20191,24 @@ function registerBalanceCommand(program) {
19234
20191
 
19235
20192
  const program = new commander.Command();
19236
20193
  program
19237
- .name("dipcoin")
20194
+ .name("dipcoin-cli")
19238
20195
  .description("DipCoin Perpetual Trading CLI")
19239
20196
  .version("0.5.0")
19240
- .option("--json", "Output in JSON format");
20197
+ .option("--json", "Output in JSON format")
20198
+ .option("--vault-index <n>", "Vault index (1+ for HD-derived sub-accounts)");
19241
20199
  registerAccountCommands(program);
19242
20200
  registerTradeCommands(program);
19243
20201
  registerPositionCommands(program);
19244
20202
  registerMarketCommands(program);
19245
20203
  registerHistoryCommands(program);
19246
20204
  registerVaultCommands(program);
20205
+ registerSubAccountCommands(program);
19247
20206
  registerOrdersCommand(program);
19248
20207
  registerBalanceCommand(program);
19249
20208
  program.parseAsync(process.argv).catch((err) => {
19250
20209
  console.error("Error:", err.message || err);
19251
20210
  process.exit(1);
19252
20211
  });
20212
+
20213
+ exports.getGlobalVaultIndex = getGlobalVaultIndex;
20214
+ //# sourceMappingURL=cli.cjs.map