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.
- package/README.md +215 -543
- package/dist/cli/commands/account.d.ts.map +1 -1
- package/dist/cli/commands/balance.d.ts.map +1 -1
- package/dist/cli/commands/history.d.ts.map +1 -1
- package/dist/cli/commands/market.d.ts.map +1 -1
- package/dist/cli/commands/orders.d.ts +3 -0
- package/dist/cli/commands/orders.d.ts.map +1 -0
- package/dist/cli/commands/position.d.ts.map +1 -1
- package/dist/cli/commands/sub-account.d.ts +3 -0
- package/dist/cli/commands/sub-account.d.ts.map +1 -0
- package/dist/cli/commands/trade.d.ts.map +1 -1
- package/dist/cli/commands/vault.d.ts +0 -1
- package/dist/cli/commands/vault.d.ts.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/utils/output.d.ts +8 -0
- package/dist/cli/utils/output.d.ts.map +1 -1
- package/dist/cli/utils/sdk-factory.d.ts +35 -2
- package/dist/cli/utils/sdk-factory.d.ts.map +1 -1
- package/dist/cli/utils/vault-index.d.ts +6 -0
- package/dist/cli/utils/vault-index.d.ts.map +1 -0
- package/dist/cli.cjs +1521 -258
- package/dist/cli.cjs.map +1 -0
- package/dist/config/deployed/testnet/main_contract.json +58 -275
- package/dist/index.cjs.js +2 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +2 -2
- package/dist/index.esm.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/constants/index.d.ts +1 -0
- package/dist/src/constants/index.d.ts.map +1 -1
- package/dist/src/onchain/exchange.d.ts +41 -0
- package/dist/src/onchain/exchange.d.ts.map +1 -0
- package/dist/src/onchain/index.d.ts +5 -0
- package/dist/src/onchain/index.d.ts.map +1 -0
- package/dist/src/onchain/order-signer.d.ts +17 -0
- package/dist/src/onchain/order-signer.d.ts.map +1 -0
- package/dist/src/onchain/transaction-builder.d.ts +46 -0
- package/dist/src/onchain/transaction-builder.d.ts.map +1 -0
- package/dist/src/sdk/sdk.d.ts +117 -4
- package/dist/src/sdk/sdk.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +6 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/utils/jwt-cache.d.ts +14 -0
- package/dist/src/utils/jwt-cache.d.ts.map +1 -0
- 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
|
|
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 === "
|
|
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
|
-
|
|
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
|
|
16988
|
+
// If already authenticated in this process, return it
|
|
16597
16989
|
if (this.jwtToken) {
|
|
16598
|
-
return {
|
|
16599
|
-
|
|
16600
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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.
|
|
18563
|
+
return executeTxBlock(this.suiClient, transaction, this.keypair);
|
|
18206
18564
|
}
|
|
18207
18565
|
const fallbackPayload = this.buildMarginCallArgs(params, "add");
|
|
18208
|
-
|
|
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.
|
|
18576
|
+
return executeTxBlock(this.suiClient, transaction, this.keypair);
|
|
18218
18577
|
}
|
|
18219
18578
|
const fallbackPayload = this.buildMarginCallArgs(params, "remove");
|
|
18220
|
-
|
|
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.
|
|
18624
|
+
return buildAddMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
|
|
18275
18625
|
}
|
|
18276
|
-
return this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
18513
|
-
|
|
18514
|
-
|
|
18515
|
-
|
|
18516
|
-
|
|
18517
|
-
|
|
18518
|
-
|
|
18519
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18531
|
-
|
|
19301
|
+
/**
|
|
19302
|
+
* Get the network from env.
|
|
19303
|
+
*/
|
|
19304
|
+
function getNetwork() {
|
|
19305
|
+
return process.env.NETWORK || "testnet";
|
|
18532
19306
|
}
|
|
18533
|
-
|
|
18534
|
-
|
|
18535
|
-
|
|
18536
|
-
|
|
18537
|
-
|
|
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
|
|
18574
|
-
|
|
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",
|
|
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
|
|
19482
|
+
const vaultIndex = getGlobalVaultIndex(program);
|
|
19483
|
+
const sdk = getOnChainSDK(vaultIndex);
|
|
18602
19484
|
const tx = await sdk.depositToBank(Number(amount));
|
|
18603
|
-
|
|
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
|
|
19497
|
+
const vaultIndex = getGlobalVaultIndex(program);
|
|
19498
|
+
const sdk = getOnChainSDK(vaultIndex);
|
|
18625
19499
|
const tx = await sdk.withdrawFromBank(Number(amount));
|
|
18626
|
-
|
|
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
|
-
|
|
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
|
|
18646
|
-
|
|
18647
|
-
const
|
|
18648
|
-
const orderType = opts.
|
|
18649
|
-
|
|
18650
|
-
|
|
18651
|
-
|
|
18652
|
-
|
|
18653
|
-
|
|
18654
|
-
|
|
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
|
|
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 ${
|
|
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 (
|
|
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
|
-
|
|
18705
|
-
|
|
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("--
|
|
18727
|
-
.option("--price <p>", "
|
|
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("
|
|
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("
|
|
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
|
-
|
|
18753
|
-
|
|
18754
|
-
const
|
|
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
|
|
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
|
|
18782
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
19010
|
-
|
|
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
|
|
19049
|
-
|
|
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", "
|
|
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.
|
|
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
|
|
19085
|
-
|
|
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", "
|
|
19099
|
-
b.type || "-",
|
|
19100
|
-
formatWei(b.amount),
|
|
19101
|
-
|
|
19102
|
-
b.
|
|
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("
|
|
20052
|
+
const vault = program.command("vault").description("On-chain vault operations");
|
|
20053
|
+
// === vault create ===
|
|
19114
20054
|
vault
|
|
19115
|
-
.command("
|
|
19116
|
-
.description("
|
|
19117
|
-
.
|
|
19118
|
-
.
|
|
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
|
-
|
|
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(
|
|
19124
|
-
|
|
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
|
|
19133
|
-
.
|
|
19134
|
-
.action(async (
|
|
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
|
|
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
|
|
19149
|
-
["
|
|
19150
|
-
["
|
|
19151
|
-
["
|
|
19152
|
-
["
|
|
19153
|
-
["
|
|
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
|
|
19170
|
-
|
|
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
|
|
19213
|
-
const
|
|
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
|