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