dipcoin-cli 1.3.0 → 1.3.1
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 +20 -5
- package/dist/cli.cjs +224 -10
- package/dist/cli.cjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,9 +101,12 @@ dipcoin-cli market pairs # List all trading pairs
|
|
|
101
101
|
dipcoin-cli market ticker <symbol> # Ticker (price, volume, funding)
|
|
102
102
|
dipcoin-cli market orderbook <symbol> # Order book
|
|
103
103
|
dipcoin-cli market oracle <symbol> # Oracle price
|
|
104
|
+
dipcoin-cli market kline <symbol> # Candlestick history (default 1h, last 24h)
|
|
105
|
+
dipcoin-cli market kline BTC --interval 5m --count 20
|
|
106
|
+
dipcoin-cli market kline BTC --interval 1d --from <unix-sec> --to <unix-sec>
|
|
104
107
|
```
|
|
105
108
|
|
|
106
|
-
Symbols auto-normalize: `BTC` becomes `BTC-PERP`.
|
|
109
|
+
Symbols auto-normalize: `BTC` becomes `BTC-PERP`. `kline` intervals: `1m / 5m / 15m / 30m / 1h / 4h / 1d / 1w`.
|
|
107
110
|
|
|
108
111
|
#### account
|
|
109
112
|
|
|
@@ -145,8 +148,14 @@ dipcoin-cli trade orders --symbol BTC-PERP
|
|
|
145
148
|
|
|
146
149
|
# Cancel orders
|
|
147
150
|
dipcoin-cli trade cancel <symbol> <hash1> [hash2...]
|
|
151
|
+
|
|
152
|
+
# Adjust default (ISOLATED) leverage for a market (REST update, no on-chain tx)
|
|
153
|
+
dipcoin-cli trade leverage BTC 10
|
|
154
|
+
dipcoin-cli trade leverage ETH 5
|
|
148
155
|
```
|
|
149
156
|
|
|
157
|
+
DipCoin perp currently only supports ISOLATED margin; CROSS is not exposed by the backend.
|
|
158
|
+
|
|
150
159
|
**Trade Options:**
|
|
151
160
|
|
|
152
161
|
| Option | Description |
|
|
@@ -172,13 +181,19 @@ dipcoin-cli position tpsl <symbol> --side <buy|sell> --quantity <q> --leverage <
|
|
|
172
181
|
dipcoin-cli position tpsl <symbol> --side sell --quantity 0.01 --leverage 10 \
|
|
173
182
|
--tp-trigger 105000 --tp-type limit --tp-price 105000
|
|
174
183
|
|
|
175
|
-
# Margin operations (on-chain)
|
|
184
|
+
# Margin operations (on-chain) — auto-prepends a fresh signed price update
|
|
176
185
|
dipcoin-cli position margin add <symbol> <amount>
|
|
177
186
|
dipcoin-cli position margin remove <symbol> <amount>
|
|
187
|
+
|
|
188
|
+
# Same, but target a vault's position
|
|
189
|
+
dipcoin-cli position margin add <symbol> <amount> --vault <vaultId>
|
|
190
|
+
dipcoin-cli position margin remove <symbol> <amount> --vault <vaultId>
|
|
178
191
|
```
|
|
179
192
|
|
|
180
193
|
The `--side` is the **closing side**: use `sell` for long positions, `buy` for short.
|
|
181
194
|
|
|
195
|
+
> All on-chain margin / vault writes (`position margin add/remove`, `vault deposit/close/withdraw/claim/fill-withdrawals`) fetch a fresh signed price from `/api/perp-market-api/price/latest` and bundle a `price_feed::update_price_feed` PTB step before the actual call — required by the v6 oracle freshness check.
|
|
196
|
+
|
|
182
197
|
#### vault
|
|
183
198
|
|
|
184
199
|
On-chain vault operations (DipCoin vault contracts):
|
|
@@ -197,9 +212,9 @@ dipcoin-cli vault position <vaultId> --address <addr> # Query another address
|
|
|
197
212
|
dipcoin-cli vault deposit <vaultId> <amount>
|
|
198
213
|
dipcoin-cli vault withdraw <vaultId> <shares>
|
|
199
214
|
dipcoin-cli vault withdraw <vaultId> --all # Withdraw all shares
|
|
200
|
-
dipcoin-cli vault fill <vaultId> <requestIDs...>
|
|
201
|
-
dipcoin-cli vault close <vaultId>
|
|
202
|
-
dipcoin-cli vault remove <vaultId>
|
|
215
|
+
dipcoin-cli vault fill-withdrawals <vaultId> <requestIDs...> # creator/operator only
|
|
216
|
+
dipcoin-cli vault close <vaultId>
|
|
217
|
+
dipcoin-cli vault remove <vaultId> # operator-only (normal creators get "unauthorized")
|
|
203
218
|
dipcoin-cli vault claim <vaultId>
|
|
204
219
|
dipcoin-cli vault set-trader <vaultId> <address>
|
|
205
220
|
dipcoin-cli vault set-sub-trader <vaultId> <address> [--disable]
|
package/dist/cli.cjs
CHANGED
|
@@ -12973,7 +12973,11 @@ function formatNormalToWei(value, decimals = 18) {
|
|
|
12973
12973
|
* @returns BigNumber instance
|
|
12974
12974
|
*/
|
|
12975
12975
|
function formatNormalToWeiBN(value, decimals = 18) {
|
|
12976
|
+
if (!value && value !== 0)
|
|
12977
|
+
return new BigNumber__default["default"](0);
|
|
12976
12978
|
const bn = new BigNumber__default["default"](value);
|
|
12979
|
+
if (bn.isNaN())
|
|
12980
|
+
return new BigNumber__default["default"](0);
|
|
12977
12981
|
return bn.multipliedBy(new BigNumber__default["default"](10).pow(decimals));
|
|
12978
12982
|
}
|
|
12979
12983
|
/**
|
|
@@ -16369,6 +16373,8 @@ const API_ENDPOINTS = {
|
|
|
16369
16373
|
FUNDING_SETTLEMENTS: "/api/perp-trade-api/history/funding-settlements",
|
|
16370
16374
|
BALANCE_CHANGES: "/api/perp-trade-api/history/balance-changes",
|
|
16371
16375
|
ORACLE: "/api/perp-market-api/oracle",
|
|
16376
|
+
SIGNED_PRICE_LATEST: "/api/perp-market-api/price/latest",
|
|
16377
|
+
KLINE_HISTORY: "/api/perp-market-api/kline/history",
|
|
16372
16378
|
VAULTS_BY_CREATOR: "/api/perp-vault-api/public/vaults/by-creator",
|
|
16373
16379
|
VAULTS_PUBLIC: "/api/perp-vault-api/public/vaults",
|
|
16374
16380
|
// Points & Referral
|
|
@@ -18558,6 +18564,77 @@ class DipCoinPerpSDK {
|
|
|
18558
18564
|
return { status: false, error: formatError(error) };
|
|
18559
18565
|
}
|
|
18560
18566
|
}
|
|
18567
|
+
/**
|
|
18568
|
+
* Fetch kline (candlestick) history for a market.
|
|
18569
|
+
*
|
|
18570
|
+
* @param params.symbol Trading pair (e.g. "BTC-PERP")
|
|
18571
|
+
* @param params.interval Bar interval (e.g. "1m" / "5m" / "1h" / "1d")
|
|
18572
|
+
* @param params.from Inclusive start timestamp (Unix seconds)
|
|
18573
|
+
* @param params.to Exclusive end timestamp (Unix seconds)
|
|
18574
|
+
* @param params.countback Optional cap on returned bars (most recent first)
|
|
18575
|
+
*/
|
|
18576
|
+
async getKlineHistory(params) {
|
|
18577
|
+
try {
|
|
18578
|
+
if (!params?.symbol)
|
|
18579
|
+
return { status: false, error: "symbol is required" };
|
|
18580
|
+
if (!params?.interval)
|
|
18581
|
+
return { status: false, error: "interval is required" };
|
|
18582
|
+
// Server requires both startTime and endTime in unix seconds.
|
|
18583
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
18584
|
+
const startTime = params.from ?? nowSec - 86400;
|
|
18585
|
+
const endTime = params.to ?? nowSec;
|
|
18586
|
+
const response = await this.httpClient.get(API_ENDPOINTS.KLINE_HISTORY, {
|
|
18587
|
+
params: {
|
|
18588
|
+
symbol: params.symbol,
|
|
18589
|
+
interval: params.interval,
|
|
18590
|
+
startTime,
|
|
18591
|
+
endTime,
|
|
18592
|
+
...(params.countback !== undefined ? { countback: params.countback } : {}),
|
|
18593
|
+
},
|
|
18594
|
+
});
|
|
18595
|
+
if (response.code !== 200) {
|
|
18596
|
+
return { status: false, error: response.message || `code ${response.code}` };
|
|
18597
|
+
}
|
|
18598
|
+
const raw = Array.isArray(response.data)
|
|
18599
|
+
? response.data
|
|
18600
|
+
: Array.isArray(response.data?.data)
|
|
18601
|
+
? response.data.data
|
|
18602
|
+
: [];
|
|
18603
|
+
const weiToNormal = (v) => {
|
|
18604
|
+
if (v === undefined || v === null || v === "")
|
|
18605
|
+
return "0";
|
|
18606
|
+
const bn = new BigNumber__default["default"](String(v));
|
|
18607
|
+
if (!bn.isFinite() || bn.isZero())
|
|
18608
|
+
return "0";
|
|
18609
|
+
return bn.dividedBy(new BigNumber__default["default"](10).pow(18)).toString();
|
|
18610
|
+
};
|
|
18611
|
+
const bars = raw.map((row) => {
|
|
18612
|
+
if (Array.isArray(row)) {
|
|
18613
|
+
const [time, open, high, low, close, volume] = row;
|
|
18614
|
+
return {
|
|
18615
|
+
time: Number(time ?? 0),
|
|
18616
|
+
open: weiToNormal(open),
|
|
18617
|
+
high: weiToNormal(high),
|
|
18618
|
+
low: weiToNormal(low),
|
|
18619
|
+
close: weiToNormal(close),
|
|
18620
|
+
volume: weiToNormal(volume),
|
|
18621
|
+
};
|
|
18622
|
+
}
|
|
18623
|
+
return {
|
|
18624
|
+
time: Number(row.time ?? row.timestamp ?? 0),
|
|
18625
|
+
open: weiToNormal(row.open),
|
|
18626
|
+
high: weiToNormal(row.high),
|
|
18627
|
+
low: weiToNormal(row.low),
|
|
18628
|
+
close: weiToNormal(row.close),
|
|
18629
|
+
volume: weiToNormal(row.volume ?? row.vol),
|
|
18630
|
+
};
|
|
18631
|
+
});
|
|
18632
|
+
return { status: true, data: bars };
|
|
18633
|
+
}
|
|
18634
|
+
catch (error) {
|
|
18635
|
+
return { status: false, error: formatError(error) };
|
|
18636
|
+
}
|
|
18637
|
+
}
|
|
18561
18638
|
/**
|
|
18562
18639
|
* Add isolated margin to an existing position (on-chain)
|
|
18563
18640
|
* @param params Margin adjustment parameters
|
|
@@ -18620,9 +18697,14 @@ class DipCoinPerpSDK {
|
|
|
18620
18697
|
}
|
|
18621
18698
|
async buildMarginTransaction(params, action) {
|
|
18622
18699
|
const payload = this.buildMarginCallArgs(params, action);
|
|
18623
|
-
// v6+
|
|
18624
|
-
//
|
|
18700
|
+
// v6+ requires PriceFeed to be fresh (~60s) before the margin call. Operator updates
|
|
18701
|
+
// are only hourly so we always fetch + prepend our own update_price_feed step.
|
|
18702
|
+
const sym = payload.market || params.symbol;
|
|
18703
|
+
if (!sym)
|
|
18704
|
+
throw new Error("symbol/market required to refresh PriceFeed before margin op");
|
|
18705
|
+
const feeds = await this.fetchSignedPriceFeeds([sym]);
|
|
18625
18706
|
const baseTx = new transactions.Transaction();
|
|
18707
|
+
this.appendPriceFeedUpdates(baseTx, Array.from(feeds.values()));
|
|
18626
18708
|
if (action === "add") {
|
|
18627
18709
|
return buildAddMarginTx(this.deploymentConfig, payload, baseTx, params.gasBudget);
|
|
18628
18710
|
}
|
|
@@ -18915,6 +18997,63 @@ class DipCoinPerpSDK {
|
|
|
18915
18997
|
}
|
|
18916
18998
|
return id;
|
|
18917
18999
|
}
|
|
19000
|
+
/**
|
|
19001
|
+
* Fetch signed price update bytes for the given markets from the DipCoin pricing service.
|
|
19002
|
+
* Each entry's payload/signature/publicKey is consumed by
|
|
19003
|
+
* `<pkg>::price_feed::update_price_feed(PriceFeed, Clock, payload, signature, publicKey)`.
|
|
19004
|
+
*
|
|
19005
|
+
* The on-chain v6 contract requires the PriceFeed entry for a perp to be fresh (~60s)
|
|
19006
|
+
* for any margin/vault NAV write. The CLI prepends one update_price_feed call per
|
|
19007
|
+
* perp involved in the tx before the actual margin/vault call.
|
|
19008
|
+
*/
|
|
19009
|
+
async fetchSignedPriceFeeds(symbols) {
|
|
19010
|
+
const out = new Map();
|
|
19011
|
+
if (symbols.length === 0)
|
|
19012
|
+
return out;
|
|
19013
|
+
const qs = symbols.map((s) => `symbols=${encodeURIComponent(s)}`).join("&");
|
|
19014
|
+
const resp = await this.httpClient.get(`${API_ENDPOINTS.SIGNED_PRICE_LATEST}?${qs}`);
|
|
19015
|
+
if (resp.code !== 200 || !resp.data) {
|
|
19016
|
+
throw new Error(`Failed to fetch signed price feed for [${symbols.join(",")}]: ${resp.message || `code ${resp.code}`}`);
|
|
19017
|
+
}
|
|
19018
|
+
const d = resp.data;
|
|
19019
|
+
const b64 = (s) => Uint8Array.from(Buffer.from(s, "base64"));
|
|
19020
|
+
if (!d.payload || !d.signature || !d.publicKey) {
|
|
19021
|
+
throw new Error("Signed price feed response missing payload/signature/publicKey");
|
|
19022
|
+
}
|
|
19023
|
+
const entry = { payload: b64(d.payload), signature: b64(d.signature), publicKey: b64(d.publicKey) };
|
|
19024
|
+
// API returns a single bundle covering all requested symbols' prices in priceList,
|
|
19025
|
+
// but only one (payload, signature, publicKey) tuple. Map every requested symbol to it.
|
|
19026
|
+
for (const sym of symbols)
|
|
19027
|
+
out.set(sym, entry);
|
|
19028
|
+
return out;
|
|
19029
|
+
}
|
|
19030
|
+
/**
|
|
19031
|
+
* Build a `price_feed::update_price_feed` PTB command for each provided signed feed
|
|
19032
|
+
* entry and append to the given transaction.
|
|
19033
|
+
*/
|
|
19034
|
+
appendPriceFeedUpdates(tx, feeds) {
|
|
19035
|
+
const pkg = this.getPackageId();
|
|
19036
|
+
const pfId = this.getPriceFeedId();
|
|
19037
|
+
for (const f of feeds) {
|
|
19038
|
+
tx.moveCall({
|
|
19039
|
+
target: `${pkg}::price_feed::update_price_feed`,
|
|
19040
|
+
arguments: [
|
|
19041
|
+
tx.object(pfId),
|
|
19042
|
+
tx.object("0x6"),
|
|
19043
|
+
tx.pure.vector("u8", Array.from(f.payload)),
|
|
19044
|
+
tx.pure.vector("u8", Array.from(f.signature)),
|
|
19045
|
+
tx.pure.vector("u8", Array.from(f.publicKey)),
|
|
19046
|
+
],
|
|
19047
|
+
typeArguments: [],
|
|
19048
|
+
});
|
|
19049
|
+
}
|
|
19050
|
+
}
|
|
19051
|
+
getPackageId() {
|
|
19052
|
+
const pkgs = this.deploymentConfig?.packages;
|
|
19053
|
+
if (!pkgs || !pkgs.length)
|
|
19054
|
+
throw new Error("Deployment config missing packages array");
|
|
19055
|
+
return pkgs[pkgs.length - 1];
|
|
19056
|
+
}
|
|
18918
19057
|
async getOnChainPerpetualIds() {
|
|
18919
19058
|
const pcId = this.getDeploymentProtocolConfigId();
|
|
18920
19059
|
const df = await this.suiClient.getDynamicFieldObject({
|
|
@@ -18935,12 +19074,16 @@ class DipCoinPerpSDK {
|
|
|
18935
19074
|
const t = tx || new transactions.Transaction();
|
|
18936
19075
|
const vaultPkg = this.getVaultPackageId();
|
|
18937
19076
|
const currencyType = this.getCurrencyType();
|
|
18938
|
-
// v6+: PriceFeed
|
|
18939
|
-
//
|
|
18940
|
-
//
|
|
18941
|
-
//
|
|
18942
|
-
// list (which can drift behind chain additions).
|
|
19077
|
+
// v6+: PriceFeed must be fresh for every perp whose value compute_perpetual_position_value_v2
|
|
19078
|
+
// will touch. Refresh ALL markets first by fetching one combined signed bundle and prepending
|
|
19079
|
+
// a single update_price_feed step. Then iterate the on-chain perpetual_registry (which is
|
|
19080
|
+
// what new_vault_nav populates, and is_nav_filled requires every entry to be computed).
|
|
18943
19081
|
const priceFeedId = this.getPriceFeedId();
|
|
19082
|
+
const allSymbols = this.getMarketSymbols();
|
|
19083
|
+
const feeds = await this.fetchSignedPriceFeeds(allSymbols);
|
|
19084
|
+
// Dedup the bundle entries (the API returns one tuple covering all requested symbols).
|
|
19085
|
+
const uniqFeeds = Array.from(new Map(Array.from(feeds.values()).map((f) => [Buffer.from(f.payload).toString("hex"), f])).values());
|
|
19086
|
+
this.appendPriceFeedUpdates(t, uniqFeeds);
|
|
18944
19087
|
const perpetualIds = await this.getOnChainPerpetualIds();
|
|
18945
19088
|
const [nav] = t.moveCall({
|
|
18946
19089
|
target: `${vaultPkg}::vault::new_vault_nav`,
|
|
@@ -19821,6 +19964,34 @@ function registerTradeCommands(program) {
|
|
|
19821
19964
|
.argument("<symbol>", "Trading pair (e.g. BTC or BTC-PERP)")
|
|
19822
19965
|
.argument("<amount>", "USDC margin amount (e.g. 100 or 100USDC)")
|
|
19823
19966
|
.argument("<leverage>", "Leverage multiplier (e.g. 10 or 10x)")).action((symbol, amount, leverage, opts) => placeOrder(program, OrderSide.SELL, symbol, amount, leverage, opts));
|
|
19967
|
+
trade
|
|
19968
|
+
.command("leverage")
|
|
19969
|
+
.description("Adjust default leverage for a market (e.g. trade leverage BTC 10)")
|
|
19970
|
+
.argument("<symbol>", "Trading pair (e.g. BTC or BTC-PERP)")
|
|
19971
|
+
.argument("<leverage>", "Leverage multiplier (e.g. 10 or 10x)")
|
|
19972
|
+
.action(async (symbol, leverage) => {
|
|
19973
|
+
try {
|
|
19974
|
+
symbol = normalizeSymbol(symbol);
|
|
19975
|
+
const lev = Number(String(leverage).replace(/x$/i, ""));
|
|
19976
|
+
if (!Number.isFinite(lev) || lev <= 0)
|
|
19977
|
+
return handleError(`Invalid leverage: ${leverage}`);
|
|
19978
|
+
const sdk = getSDK();
|
|
19979
|
+
// Only ISOLATED margin is supported by the backend today.
|
|
19980
|
+
const result = await sdk.adjustLeverage({
|
|
19981
|
+
symbol,
|
|
19982
|
+
leverage: lev,
|
|
19983
|
+
marginType: "ISOLATED",
|
|
19984
|
+
});
|
|
19985
|
+
if (!result.status)
|
|
19986
|
+
return handleError(result.error);
|
|
19987
|
+
if (isJson(program))
|
|
19988
|
+
return printJson(result.data);
|
|
19989
|
+
console.log(`Leverage for ${symbol} set to ${lev}x (ISOLATED).`);
|
|
19990
|
+
}
|
|
19991
|
+
catch (e) {
|
|
19992
|
+
handleError(e);
|
|
19993
|
+
}
|
|
19994
|
+
});
|
|
19824
19995
|
trade
|
|
19825
19996
|
.command("cancel")
|
|
19826
19997
|
.description("Cancel orders by hash")
|
|
@@ -20128,6 +20299,48 @@ function registerMarketCommands(program) {
|
|
|
20128
20299
|
handleError(e);
|
|
20129
20300
|
}
|
|
20130
20301
|
});
|
|
20302
|
+
market
|
|
20303
|
+
.command("kline")
|
|
20304
|
+
.description("Get kline (candlestick) history for a symbol")
|
|
20305
|
+
.argument("<symbol>", "Trading pair")
|
|
20306
|
+
.option("--interval <i>", "Bar interval: 1m / 5m / 15m / 30m / 1h / 4h / 1d / 1w (default: 1h)", "1h")
|
|
20307
|
+
.option("--from <ts>", "Start unix timestamp in seconds (default: 24h ago)")
|
|
20308
|
+
.option("--to <ts>", "End unix timestamp in seconds (default: now)")
|
|
20309
|
+
.option("--count <n>", "Cap on number of bars returned (most recent first)")
|
|
20310
|
+
.action(async (symbol, opts) => {
|
|
20311
|
+
try {
|
|
20312
|
+
symbol = normalizeSymbol(symbol);
|
|
20313
|
+
const now = Math.floor(Date.now() / 1000);
|
|
20314
|
+
const from = opts.from !== undefined ? Number(opts.from) : now - 86400;
|
|
20315
|
+
const to = opts.to !== undefined ? Number(opts.to) : now;
|
|
20316
|
+
const countback = opts.count !== undefined ? Number(opts.count) : undefined;
|
|
20317
|
+
const sdk = getSDK();
|
|
20318
|
+
const result = await sdk.getKlineHistory({
|
|
20319
|
+
symbol,
|
|
20320
|
+
interval: String(opts.interval),
|
|
20321
|
+
from,
|
|
20322
|
+
to,
|
|
20323
|
+
countback,
|
|
20324
|
+
});
|
|
20325
|
+
if (!result.status || !result.data)
|
|
20326
|
+
return handleError(result.error);
|
|
20327
|
+
if (isJson(program))
|
|
20328
|
+
return printJson(result.data);
|
|
20329
|
+
if (!result.data.length)
|
|
20330
|
+
return console.log("No bars.");
|
|
20331
|
+
printTable(["Time (UTC)", "Open", "High", "Low", "Close", "Volume"], result.data.map((b) => [
|
|
20332
|
+
new Date(b.time * 1000).toISOString().replace("T", " ").slice(0, 16),
|
|
20333
|
+
b.open,
|
|
20334
|
+
b.high,
|
|
20335
|
+
b.low,
|
|
20336
|
+
b.close,
|
|
20337
|
+
b.volume,
|
|
20338
|
+
]));
|
|
20339
|
+
}
|
|
20340
|
+
catch (e) {
|
|
20341
|
+
handleError(e);
|
|
20342
|
+
}
|
|
20343
|
+
});
|
|
20131
20344
|
}
|
|
20132
20345
|
|
|
20133
20346
|
function registerHistoryCommands(program) {
|
|
@@ -20556,11 +20769,12 @@ function registerVaultCommands(program) {
|
|
|
20556
20769
|
});
|
|
20557
20770
|
// === vault fill ===
|
|
20558
20771
|
vault
|
|
20559
|
-
.command("fill")
|
|
20560
|
-
.
|
|
20772
|
+
.command("fill-withdrawals")
|
|
20773
|
+
.alias("fill")
|
|
20774
|
+
.description("Fill pending withdrawal requests (creator/operator only) — e.g. vault fill-withdrawals <vaultId> <requestIDs...>")
|
|
20561
20775
|
.argument("<vaultId>", "Vault object ID")
|
|
20562
20776
|
.argument("<requestIDs...>", "Withdrawal request object IDs")
|
|
20563
|
-
.option("--markets <ids>", "Comma-separated market PerpetualIDs
|
|
20777
|
+
.option("--markets <ids>", "Comma-separated market PerpetualIDs (deprecated; NAV now uses on-chain registry)")
|
|
20564
20778
|
.action(async (vaultId, requestIDs, opts) => {
|
|
20565
20779
|
try {
|
|
20566
20780
|
const sdk = getSDK();
|