perp-cli 0.12.15 → 0.14.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 +278 -31
- package/dist/_stderr-filter.js +0 -1
- package/dist/agent-wallet/lighter-keystore.d.ts +11 -0
- package/dist/agent-wallet/lighter-keystore.js +92 -10
- package/dist/agent-wallet/passphrase.d.ts +1 -1
- package/dist/agent-wallet/passphrase.js +16 -3
- package/dist/agent-wallet/store.d.ts +37 -4
- package/dist/agent-wallet/store.js +132 -0
- package/dist/agent-wallet/venue-keystore.d.ts +14 -0
- package/dist/agent-wallet/venue-keystore.js +121 -0
- package/dist/api/public/hyperliquid.js +4 -4
- package/dist/api/public/lighter.js +5 -5
- package/dist/api/public/pacifica.d.ts +0 -1
- package/dist/api/public/pacifica.js +4 -13
- package/dist/api/public/urls.d.ts +9 -1
- package/dist/api/public/urls.js +35 -13
- package/dist/arb/file-lock.d.ts +11 -0
- package/dist/arb/file-lock.js +74 -0
- package/dist/arb/index.d.ts +2 -2
- package/dist/arb/index.js +2 -2
- package/dist/arb/pending-entry.d.ts +32 -0
- package/dist/arb/pending-entry.js +77 -0
- package/dist/arb/sizing.d.ts +6 -13
- package/dist/arb/sizing.js +12 -51
- package/dist/arb/state.d.ts +45 -12
- package/dist/arb/state.js +214 -42
- package/dist/arb/utils.d.ts +20 -6
- package/dist/arb/utils.js +43 -39
- package/dist/bot/conditions.d.ts +4 -4
- package/dist/bot/conditions.js +11 -6
- package/dist/bot/config.d.ts +17 -1
- package/dist/bot/config.js +48 -12
- package/dist/bot/engine.d.ts +61 -2
- package/dist/bot/engine.js +610 -144
- package/dist/bot/fill-size.d.ts +7 -0
- package/dist/bot/fill-size.js +18 -0
- package/dist/bot/index.d.ts +1 -1
- package/dist/bot/index.js +1 -1
- package/dist/bot/perp-order.d.ts +99 -0
- package/dist/bot/perp-order.js +299 -0
- package/dist/bot/presets.d.ts +7 -0
- package/dist/bot/presets.js +17 -0
- package/dist/bot/quarantine.d.ts +75 -0
- package/dist/bot/quarantine.js +190 -0
- package/dist/bot/quote-rules.d.ts +23 -0
- package/dist/bot/quote-rules.js +69 -0
- package/dist/bot/strategies/avellaneda-mm.d.ts +32 -2
- package/dist/bot/strategies/avellaneda-mm.js +72 -70
- package/dist/bot/strategies/basis-arb.js +2 -0
- package/dist/bot/strategies/dca-strategy.d.ts +2 -0
- package/dist/bot/strategies/dca-strategy.js +5 -0
- package/dist/bot/strategies/funding-arb-strategy.d.ts +43 -3
- package/dist/bot/strategies/funding-arb-strategy.js +571 -380
- package/dist/bot/strategies/funding-arb-utils.d.ts +48 -23
- package/dist/bot/strategies/funding-arb-utils.js +159 -195
- package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
- package/dist/bot/strategies/funding-arb-v2.js +258 -47
- package/dist/bot/strategies/funding-auto.js +215 -194
- package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +14 -0
- package/dist/bot/strategies/spot-perp-arb-strategy.js +217 -50
- package/dist/bot/strategies/spot-perp-executor.d.ts +73 -19
- package/dist/bot/strategies/spot-perp-executor.js +319 -120
- package/dist/bot/strategies/spot-perp-state.d.ts +43 -0
- package/dist/bot/strategies/spot-perp-state.js +70 -0
- package/dist/bot/strategy-types.d.ts +6 -0
- package/dist/bot/tui/BotDashboard.d.ts +7 -2
- package/dist/bot/tui/BotDashboard.js +6 -3
- package/dist/bridge-engine.d.ts +120 -19
- package/dist/bridge-engine.js +710 -422
- package/dist/bridge-signers.d.ts +54 -0
- package/dist/bridge-signers.js +161 -0
- package/dist/cache.d.ts +4 -1
- package/dist/cache.js +11 -0
- package/dist/cli-flags.d.ts +50 -0
- package/dist/cli-flags.js +104 -0
- package/dist/cli-program.d.ts +35 -0
- package/dist/cli-program.js +286 -0
- package/dist/cli-spec.js +1 -1
- package/dist/client-id-tracker.js +11 -2
- package/dist/commands/account.d.ts +7 -1
- package/dist/commands/account.js +74 -84
- package/dist/commands/agent-import.d.ts +114 -0
- package/dist/commands/agent-import.js +794 -0
- package/dist/commands/agent.d.ts +232 -2
- package/dist/commands/agent.js +1858 -955
- package/dist/commands/alerts.d.ts +1 -0
- package/dist/commands/alerts.js +31 -28
- package/dist/commands/arb/index.d.ts +60 -1
- package/dist/commands/arb/index.js +381 -384
- package/dist/commands/arb-auto.d.ts +132 -4
- package/dist/commands/arb-auto.js +1474 -967
- package/dist/commands/arb.d.ts +3 -1
- package/dist/commands/arb.js +62 -30
- package/dist/commands/bot.js +127 -47
- package/dist/commands/bridge.d.ts +2 -1
- package/dist/commands/bridge.js +339 -192
- package/dist/commands/builder.d.ts +55 -0
- package/dist/commands/builder.js +387 -0
- package/dist/commands/funds-onchain.d.ts +32 -0
- package/dist/commands/funds-onchain.js +286 -0
- package/dist/commands/funds.d.ts +25 -0
- package/dist/commands/funds.js +939 -245
- package/dist/commands/history.js +72 -45
- package/dist/commands/init.d.ts +107 -15
- package/dist/commands/init.js +1116 -249
- package/dist/commands/jobs.d.ts +2 -1
- package/dist/commands/jobs.js +77 -15
- package/dist/commands/manage.js +136 -91
- package/dist/commands/market.d.ts +4 -1
- package/dist/commands/market.js +35 -32
- package/dist/commands/outcome.d.ts +10 -0
- package/dist/commands/outcome.js +396 -0
- package/dist/commands/plan.js +52 -12
- package/dist/commands/portfolio.d.ts +1 -0
- package/dist/commands/portfolio.js +60 -72
- package/dist/commands/rebalance.d.ts +2 -1
- package/dist/commands/rebalance.js +33 -43
- package/dist/commands/risk.js +46 -19
- package/dist/commands/settings.d.ts +1 -0
- package/dist/commands/settings.js +246 -51
- package/dist/commands/trade.d.ts +50 -0
- package/dist/commands/trade.js +1943 -695
- package/dist/commands/wallet.d.ts +30 -1
- package/dist/commands/wallet.js +316 -337
- package/dist/config.d.ts +60 -9
- package/dist/config.js +252 -82
- package/dist/constants.d.ts +29 -4
- package/dist/constants.js +62 -20
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.js +9 -6
- package/dist/dashboard/ui.js +6 -4
- package/dist/dashboard/ws-feeds.js +20 -8
- package/dist/dex-asset-map.js +5 -3
- package/dist/errors.d.ts +122 -0
- package/dist/errors.js +194 -0
- package/dist/event-stream.js +75 -19
- package/dist/evm-master-tx.d.ts +59 -0
- package/dist/evm-master-tx.js +154 -0
- package/dist/exchanges/adapter-network.d.ts +10 -0
- package/dist/exchanges/adapter-network.js +10 -0
- package/dist/exchanges/arcus/adapter.d.ts +196 -0
- package/dist/exchanges/arcus/adapter.js +1092 -0
- package/dist/exchanges/arcus/agent.d.ts +55 -0
- package/dist/exchanges/arcus/agent.js +188 -0
- package/dist/exchanges/arcus/client.d.ts +63 -0
- package/dist/exchanges/arcus/client.js +285 -0
- package/dist/exchanges/arcus/public.d.ts +65 -0
- package/dist/exchanges/arcus/public.js +138 -0
- package/dist/exchanges/arcus/signing.d.ts +176 -0
- package/dist/exchanges/arcus/signing.js +288 -0
- package/dist/exchanges/arcus/spot-chain.d.ts +60 -0
- package/dist/exchanges/arcus/spot-chain.js +241 -0
- package/dist/exchanges/arcus/spot-router.d.ts +177 -0
- package/dist/exchanges/arcus/spot-router.js +183 -0
- package/dist/exchanges/arcus/spot.d.ts +109 -0
- package/dist/exchanges/arcus/spot.js +645 -0
- package/dist/exchanges/aster-funds.d.ts +127 -0
- package/dist/exchanges/aster-funds.js +252 -0
- package/dist/exchanges/aster-nonce.d.ts +2 -0
- package/dist/exchanges/aster-nonce.js +107 -0
- package/dist/exchanges/aster-spot.d.ts +117 -0
- package/dist/exchanges/aster-spot.js +686 -0
- package/dist/exchanges/aster-typed-data.d.ts +133 -7
- package/dist/exchanges/aster-typed-data.js +121 -7
- package/dist/exchanges/aster.d.ts +219 -6
- package/dist/exchanges/aster.js +1023 -153
- package/dist/exchanges/builder-fee.d.ts +254 -0
- package/dist/exchanges/builder-fee.js +421 -0
- package/dist/exchanges/canonical-symbols.d.ts +32 -0
- package/dist/exchanges/canonical-symbols.js +96 -0
- package/dist/exchanges/capabilities.d.ts +7 -1
- package/dist/exchanges/capabilities.js +7 -0
- package/dist/exchanges/hl-signing.d.ts +165 -0
- package/dist/exchanges/hl-signing.js +158 -0
- package/dist/exchanges/hyperliquid-outcome.d.ts +211 -0
- package/dist/exchanges/hyperliquid-outcome.js +646 -0
- package/dist/exchanges/hyperliquid-spot.d.ts +44 -5
- package/dist/exchanges/hyperliquid-spot.js +308 -117
- package/dist/exchanges/hyperliquid.d.ts +462 -57
- package/dist/exchanges/hyperliquid.js +1575 -449
- package/dist/exchanges/index.d.ts +6 -0
- package/dist/exchanges/index.js +4 -0
- package/dist/exchanges/interface.d.ts +63 -10
- package/dist/exchanges/interface.js +18 -3
- package/dist/exchanges/ioc-outcome.d.ts +44 -0
- package/dist/exchanges/ioc-outcome.js +57 -0
- package/dist/exchanges/legacy-trading-v2.d.ts +39 -0
- package/dist/exchanges/legacy-trading-v2.js +940 -0
- package/dist/exchanges/lighter-public.d.ts +43 -0
- package/dist/exchanges/lighter-public.js +276 -0
- package/dist/exchanges/lighter-rh-deposit.d.ts +62 -0
- package/dist/exchanges/lighter-rh-deposit.js +185 -0
- package/dist/exchanges/lighter-signer.d.ts +123 -0
- package/dist/exchanges/lighter-signer.js +215 -0
- package/dist/exchanges/lighter-spot.d.ts +91 -13
- package/dist/exchanges/lighter-spot.js +595 -233
- package/dist/exchanges/lighter.d.ts +611 -39
- package/dist/exchanges/lighter.js +1890 -649
- package/dist/exchanges/ondo/adapter.d.ts +166 -0
- package/dist/exchanges/ondo/adapter.js +835 -0
- package/dist/exchanges/ondo/agent.d.ts +31 -0
- package/dist/exchanges/ondo/agent.js +101 -0
- package/dist/exchanges/ondo/client.d.ts +60 -0
- package/dist/exchanges/ondo/client.js +297 -0
- package/dist/exchanges/ondo/funds.d.ts +122 -0
- package/dist/exchanges/ondo/funds.js +290 -0
- package/dist/exchanges/ondo/public.d.ts +95 -0
- package/dist/exchanges/ondo/public.js +214 -0
- package/dist/exchanges/ondo/spot.d.ts +127 -0
- package/dist/exchanges/ondo/spot.js +392 -0
- package/dist/exchanges/outcome-interface.d.ts +140 -0
- package/dist/exchanges/outcome-interface.js +16 -0
- package/dist/exchanges/pacifica-spot.d.ts +83 -0
- package/dist/exchanges/pacifica-spot.js +324 -0
- package/dist/exchanges/pacifica-typed-data.d.ts +30 -13
- package/dist/exchanges/pacifica-typed-data.js +57 -9
- package/dist/exchanges/pacifica.d.ts +288 -4
- package/dist/exchanges/pacifica.js +1013 -76
- package/dist/exchanges/precision.d.ts +47 -0
- package/dist/exchanges/precision.js +209 -0
- package/dist/exchanges/qfex/adapter.d.ts +341 -0
- package/dist/exchanges/qfex/adapter.js +1383 -0
- package/dist/exchanges/qfex/agent.d.ts +58 -0
- package/dist/exchanges/qfex/agent.js +188 -0
- package/dist/exchanges/qfex/client.d.ts +54 -0
- package/dist/exchanges/qfex/client.js +138 -0
- package/dist/exchanges/qfex/errors.d.ts +70 -0
- package/dist/exchanges/qfex/errors.js +236 -0
- package/dist/exchanges/qfex/public.d.ts +122 -0
- package/dist/exchanges/qfex/public.js +268 -0
- package/dist/exchanges/qfex/signing.d.ts +160 -0
- package/dist/exchanges/qfex/signing.js +283 -0
- package/dist/exchanges/qfex/ws-transport.d.ts +294 -0
- package/dist/exchanges/qfex/ws-transport.js +798 -0
- package/dist/exchanges/rate-limiter.d.ts +12 -0
- package/dist/exchanges/rate-limiter.js +64 -0
- package/dist/exchanges/registry.d.ts +14 -2
- package/dist/exchanges/registry.js +20 -32
- package/dist/exchanges/slippage.d.ts +37 -0
- package/dist/exchanges/slippage.js +70 -0
- package/dist/exchanges/spot-factory.d.ts +166 -0
- package/dist/exchanges/spot-factory.js +431 -0
- package/dist/exchanges/spot-interface.d.ts +98 -4
- package/dist/exchanges/spot-interface.js +27 -1
- package/dist/exchanges/spot-result.d.ts +79 -0
- package/dist/exchanges/spot-result.js +273 -0
- package/dist/exchanges/trading-v2.d.ts +117 -0
- package/dist/exchanges/trading-v2.js +301 -0
- package/dist/exchanges/venue-types.d.ts +231 -0
- package/dist/exchanges/venue-types.js +87 -0
- package/dist/exchanges/venue.d.ts +211 -0
- package/dist/exchanges/venue.js +132 -0
- package/dist/exchanges/venues/arcus.d.ts +8 -0
- package/dist/exchanges/venues/arcus.js +109 -0
- package/dist/exchanges/venues/aster.d.ts +49 -0
- package/dist/exchanges/venues/aster.js +176 -0
- package/dist/exchanges/venues/create-helpers.d.ts +18 -0
- package/dist/exchanges/venues/create-helpers.js +199 -0
- package/dist/exchanges/venues/hyperliquid.d.ts +23 -0
- package/dist/exchanges/venues/hyperliquid.js +126 -0
- package/dist/exchanges/venues/index.d.ts +16 -0
- package/dist/exchanges/venues/index.js +26 -0
- package/dist/exchanges/venues/lighter-family.d.ts +92 -0
- package/dist/exchanges/venues/lighter-family.js +166 -0
- package/dist/exchanges/venues/lighter-rh.d.ts +7 -0
- package/dist/exchanges/venues/lighter-rh.js +58 -0
- package/dist/exchanges/venues/lighter.d.ts +6 -0
- package/dist/exchanges/venues/lighter.js +55 -0
- package/dist/exchanges/venues/ondo.d.ts +34 -0
- package/dist/exchanges/venues/ondo.js +140 -0
- package/dist/exchanges/venues/pacifica.d.ts +8 -0
- package/dist/exchanges/venues/pacifica.js +95 -0
- package/dist/exchanges/venues/qfex.d.ts +28 -0
- package/dist/exchanges/venues/qfex.js +132 -0
- package/dist/execution-log.js +10 -2
- package/dist/funding/legacy-public.d.ts +31 -0
- package/dist/funding/legacy-public.js +369 -0
- package/dist/funding/normalize.d.ts +28 -16
- package/dist/funding/normalize.js +61 -29
- package/dist/funding/rates.d.ts +46 -11
- package/dist/funding/rates.js +228 -277
- package/dist/guardrail/perp-guardrail.js +0 -0
- package/dist/index.js +157 -682
- package/dist/jobs.d.ts +84 -6
- package/dist/jobs.js +239 -41
- package/dist/landing.d.ts +14 -0
- package/dist/landing.js +36 -0
- package/dist/mcp-server.d.ts +14 -4
- package/dist/mcp-server.js +820 -402
- package/dist/mcp-trade-guard.d.ts +16 -0
- package/dist/mcp-trade-guard.js +59 -0
- package/dist/pacifica/client.d.ts +41 -10
- package/dist/pacifica/client.js +167 -63
- package/dist/pacifica/constants.d.ts +0 -1
- package/dist/pacifica/constants.js +0 -5
- package/dist/pacifica/deposit.d.ts +30 -3
- package/dist/pacifica/deposit.js +119 -2
- package/dist/pacifica/faucet.d.ts +29 -0
- package/dist/pacifica/faucet.js +100 -0
- package/dist/pacifica/sign-tx.d.ts +15 -0
- package/dist/pacifica/sign-tx.js +41 -0
- package/dist/pacifica/types/account.d.ts +28 -4
- package/dist/pacifica/types/lake.d.ts +20 -5
- package/dist/pacifica/types/lake.js +4 -0
- package/dist/pacifica/types/market.d.ts +5 -0
- package/dist/pacifica/types/order.d.ts +4 -1
- package/dist/pacifica/types/position.d.ts +9 -1
- package/dist/plan-executor.d.ts +10 -5
- package/dist/plan-executor.js +165 -54
- package/dist/position-history.js +13 -0
- package/dist/rebalance.d.ts +26 -2
- package/dist/rebalance.js +86 -7
- package/dist/relay-swap.d.ts +232 -0
- package/dist/relay-swap.js +630 -0
- package/dist/risk.js +5 -1
- package/dist/secret-env.d.ts +15 -0
- package/dist/secret-env.js +36 -0
- package/dist/settings.d.ts +169 -6
- package/dist/settings.js +219 -38
- package/dist/signer/ows-evm.d.ts +13 -0
- package/dist/signer/ows-evm.js +22 -0
- package/dist/smart-order.d.ts +9 -0
- package/dist/smart-order.js +53 -8
- package/dist/strategies/dca.d.ts +6 -2
- package/dist/strategies/dca.js +66 -26
- package/dist/strategies/funding-arb.d.ts +11 -1
- package/dist/strategies/funding-arb.js +202 -89
- package/dist/strategies/grid.d.ts +31 -0
- package/dist/strategies/grid.js +89 -11
- package/dist/strategies/pending-stop.d.ts +8 -0
- package/dist/strategies/pending-stop.js +12 -0
- package/dist/strategies/split-order.d.ts +45 -17
- package/dist/strategies/split-order.js +157 -91
- package/dist/strategies/trailing-stop.d.ts +2 -1
- package/dist/strategies/trailing-stop.js +52 -6
- package/dist/strategies/twap.d.ts +6 -1
- package/dist/strategies/twap.js +39 -11
- package/dist/trade-validator.d.ts +22 -0
- package/dist/trade-validator.js +119 -15
- package/dist/utils/numeric.d.ts +46 -0
- package/dist/utils/numeric.js +79 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.js +96 -19
- package/dist/withdraw-gate.d.ts +49 -0
- package/dist/withdraw-gate.js +131 -0
- package/package.json +15 -6
- package/skills/perp-cli/SKILL.md +338 -134
- package/skills/perp-cli/references/agent-operations.md +181 -92
- package/skills/perp-cli/references/commands.md +475 -132
- package/skills/perp-cli/references/strategies.md +44 -38
- package/skills/perp-cli/scripts/arb-monitor.sh +22 -17
- package/skills/perp-cli/scripts/funding-analysis.sh +20 -10
- package/skills/perp-cli/scripts/preflight.sh +21 -14
- package/skills/perp-cli/scripts/spot-perp-scan.sh +12 -7
- package/skills/perp-cli/scripts/validate-arb.sh +33 -16
- package/vendor/lighter-signer/SHA256SUMS +2 -0
- package/vendor/lighter-signer/VERSION +8 -0
- package/vendor/lighter-signer/lighter-signer.wasm +0 -0
- package/vendor/lighter-signer/wasm_exec.js +575 -0
- package/dist/bot/strategies/funding-arb-v2-strategy.d.ts +0 -42
- package/dist/bot/strategies/funding-arb-v2-strategy.js +0 -864
package/dist/commands/jobs.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
|
|
2
|
+
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
3
|
+
export declare function registerBackgroundCommands(program: Command, isJson: () => boolean, getAdapterFor?: (venue: string) => Promise<ExchangeAdapter>): void;
|
package/dist/commands/jobs.js
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
|
-
import { listJobs, stopJob, removeJob, loadJob, logFile } from "../jobs.js";
|
|
2
|
-
import { printJson, jsonOk } from "../utils.js";
|
|
1
|
+
import { listJobs, stopJob, removeJob, loadJob, logFile, assertTmuxSessionName, JOB_STOP_TIMEOUT_MS } from "../jobs.js";
|
|
2
|
+
import { printJson, jsonOk, symbolMatch } from "../utils.js";
|
|
3
|
+
import { PerpError } from "../errors.js";
|
|
3
4
|
import chalk from "chalk";
|
|
4
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
-
import {
|
|
6
|
-
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { scrubSecretEnv } from "../secret-env.js";
|
|
8
|
+
/**
|
|
9
|
+
* What a stopped job left on its venue(s): open orders and positions on its symbol (every symbol
|
|
10
|
+
* when the job has none). One entry per venue; a failed read is reported, never "nothing left".
|
|
11
|
+
*/
|
|
12
|
+
async function leftoversOf(job, getAdapterFor) {
|
|
13
|
+
const sym = typeof job.params.symbol === "string" && job.params.symbol !== "ALL" ? job.params.symbol.toUpperCase() : null;
|
|
14
|
+
const venues = job.exchange === "multi" ? [] : job.exchange.split(/[+,]/).map(v => v.trim()).filter(Boolean);
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const venue of venues) {
|
|
17
|
+
try {
|
|
18
|
+
const a = await getAdapterFor(venue);
|
|
19
|
+
const [orders, positions] = await Promise.all([a.getOpenOrders(), a.getPositions()]);
|
|
20
|
+
const mine = (xs) => sym === null ? xs : xs.filter(x => symbolMatch(x.symbol, sym));
|
|
21
|
+
out.push({ venue, orders: mine(orders), positions: mine(positions).filter(p => Number(p.size) !== 0), error: null });
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
out.push({ venue, orders: [], positions: [], error: e instanceof Error ? e.message : String(e) });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
export function registerBackgroundCommands(program, isJson, getAdapterFor) {
|
|
7
30
|
const jobs = program.command("background").description("Background process supervisor (tmux sessions for strategies, alerts, etc.)");
|
|
8
31
|
jobs
|
|
9
32
|
.command("list", { isDefault: true })
|
|
@@ -41,23 +64,60 @@ export function registerBackgroundCommands(program, isJson) {
|
|
|
41
64
|
});
|
|
42
65
|
jobs
|
|
43
66
|
.command("stop <id>")
|
|
44
|
-
.description("Stop a running job")
|
|
45
|
-
.
|
|
67
|
+
.description("Stop a running job gracefully: SIGINT (the job cancels its orders / saves state), wait up to --timeout, only then kill; reports what is left on the venue")
|
|
68
|
+
.option("--timeout <sec>", "Seconds to wait for the job's own cleanup before killing it", String(JOB_STOP_TIMEOUT_MS / 1000))
|
|
69
|
+
.action(async (id, opts) => {
|
|
46
70
|
const job = loadJob(id);
|
|
47
71
|
if (!job) {
|
|
48
72
|
console.log(chalk.red(`\n Job ${id} not found.\n`));
|
|
49
73
|
return;
|
|
50
74
|
}
|
|
51
|
-
|
|
75
|
+
const timeoutSec = Number(opts.timeout);
|
|
76
|
+
if (!(timeoutSec > 0))
|
|
77
|
+
throw new PerpError("INVALID_PARAMS", `--timeout must be a positive number of seconds (got ${opts.timeout})`);
|
|
78
|
+
if (!isJson())
|
|
79
|
+
console.log(chalk.gray(`\n Stopping job ${id} (SIGINT, waiting up to ${timeoutSec}s for its cleanup)...`));
|
|
80
|
+
const r = await stopJob(id, timeoutSec * 1000);
|
|
81
|
+
if (r === null)
|
|
82
|
+
return;
|
|
83
|
+
const log = logFile(id);
|
|
84
|
+
const tail = existsSync(log) ? readFileSync(log, "utf-8").split("\n").filter(l => l.trim() !== "").slice(-12) : [];
|
|
85
|
+
const left = getAdapterFor !== undefined ? await leftoversOf(job, getAdapterFor) : [];
|
|
86
|
+
const leftCount = left.reduce((n, v) => n + v.orders.length + v.positions.length, 0);
|
|
52
87
|
if (isJson())
|
|
53
|
-
return printJson(jsonOk({ id, status: "stopped" }));
|
|
54
|
-
|
|
88
|
+
return printJson(jsonOk({ id, status: "stopped", stop: r, leftovers: left, logTail: tail }));
|
|
89
|
+
if (r.outcome === "forced")
|
|
90
|
+
console.log(chalk.red.bold(` Job ${id} was FORCE-KILLED: ${r.note}. Its own cleanup (cancel orders / save state) did NOT run.`));
|
|
91
|
+
else if (r.outcome === "graceful")
|
|
92
|
+
console.log(chalk.green(` Job ${id} exited after SIGINT (${(r.waitedMs / 1000).toFixed(1)}s).`));
|
|
93
|
+
else
|
|
94
|
+
console.log(chalk.gray(` Job ${id} was not running.`));
|
|
95
|
+
if (tail.length > 0) {
|
|
96
|
+
console.log(chalk.gray(" Last log lines:"));
|
|
97
|
+
for (const l of tail)
|
|
98
|
+
console.log(chalk.gray(` ${l}`));
|
|
99
|
+
}
|
|
100
|
+
for (const v of left) {
|
|
101
|
+
if (v.error !== null) {
|
|
102
|
+
console.log(chalk.yellow(` ${v.venue}: could not read what is left (${v.error}) — check: perp -e ${v.venue} account orders / account positions`));
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
for (const o of v.orders)
|
|
106
|
+
console.log(chalk.yellow(` LEFT OPEN on ${v.venue}: order ${o.side} ${o.size} ${o.symbol} @ ${o.price} (${o.orderId}) — cancel: perp -e ${v.venue} trade cancel ${o.symbol} ${o.orderId}`));
|
|
107
|
+
for (const p of v.positions)
|
|
108
|
+
console.log(chalk.yellow(` LEFT OPEN on ${v.venue}: position ${p.side} ${p.size.replace(/^-/, "")} ${p.symbol} — close: perp -e ${v.venue} trade close ${p.symbol}`));
|
|
109
|
+
}
|
|
110
|
+
if (left.length > 0 && leftCount === 0 && left.every(v => v.error === null))
|
|
111
|
+
console.log(chalk.green(` Nothing left open on ${left.map(v => v.venue).join(", ")}${typeof job.params.symbol === "string" ? ` for ${job.params.symbol}` : ""}.`));
|
|
112
|
+
if (job.exchange === "multi")
|
|
113
|
+
console.log(chalk.gray(" Multi-venue job: check every venue: perp arb status"));
|
|
114
|
+
console.log();
|
|
55
115
|
});
|
|
56
116
|
jobs
|
|
57
117
|
.command("logs <id>")
|
|
58
118
|
.description("Show logs for a job")
|
|
59
119
|
.option("-f, --follow", "Follow log output (tail -f)")
|
|
60
|
-
.option("
|
|
120
|
+
.option("--lines <n>", "Number of lines to show", "50")
|
|
61
121
|
.action((id, opts) => {
|
|
62
122
|
const log = logFile(id);
|
|
63
123
|
if (!existsSync(log)) {
|
|
@@ -69,13 +129,13 @@ export function registerBackgroundCommands(program, isJson) {
|
|
|
69
129
|
const job = loadJob(id);
|
|
70
130
|
if (job?.status === "running") {
|
|
71
131
|
try {
|
|
72
|
-
|
|
132
|
+
execFileSync("tmux", ["attach-session", "-t", assertTmuxSessionName(job.tmuxSession)], { stdio: "inherit", env: scrubSecretEnv(process.env) });
|
|
73
133
|
return;
|
|
74
134
|
}
|
|
75
135
|
catch { /* fall through to tail */ }
|
|
76
136
|
}
|
|
77
137
|
try {
|
|
78
|
-
|
|
138
|
+
execFileSync("tail", ["-f", log], { stdio: "inherit", env: scrubSecretEnv(process.env) });
|
|
79
139
|
}
|
|
80
140
|
catch { /* ctrl-c */ }
|
|
81
141
|
}
|
|
@@ -89,15 +149,17 @@ export function registerBackgroundCommands(program, isJson) {
|
|
|
89
149
|
});
|
|
90
150
|
jobs
|
|
91
151
|
.command("remove <id>")
|
|
92
|
-
.description("Remove a job entry and its logs")
|
|
93
|
-
.action((id) => {
|
|
152
|
+
.description("Remove a job entry and its logs (a running job is stopped gracefully first)")
|
|
153
|
+
.action(async (id) => {
|
|
94
154
|
const job = loadJob(id);
|
|
95
155
|
if (!job) {
|
|
96
156
|
console.log(chalk.red(`\n Job ${id} not found.\n`));
|
|
97
157
|
return;
|
|
98
158
|
}
|
|
99
159
|
if (job.status === "running") {
|
|
100
|
-
stopJob(id);
|
|
160
|
+
const r = await stopJob(id);
|
|
161
|
+
if (r?.outcome === "forced" && !isJson())
|
|
162
|
+
console.log(chalk.red(` Job ${id} was force-killed (${r.note}); its cleanup did not run — check its venue for open orders / positions.`));
|
|
101
163
|
}
|
|
102
164
|
removeJob(id);
|
|
103
165
|
if (isJson())
|
package/dist/commands/manage.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDryRun, networkOf, supportsDryRun } from "../cli-flags.js";
|
|
2
|
+
import { printJson, jsonOk, jsonError, withJsonErrors } from "../utils.js";
|
|
2
3
|
import chalk from "chalk";
|
|
3
4
|
import { hasPacificaSdk } from "../exchanges/capabilities.js";
|
|
4
|
-
import { PerpError } from "../errors.js";
|
|
5
|
-
import {
|
|
5
|
+
import { PerpError, classifyError, parsePositiveDecimal } from "../errors.js";
|
|
6
|
+
import { unsupported } from "../exchanges/trading-v2.js";
|
|
7
|
+
import { loadSettings, saveSettings } from "../settings.js";
|
|
6
8
|
import { resolvePassphrase } from "../agent-wallet/passphrase.js";
|
|
7
9
|
import { runHlSetAbstractionFlow } from "./agent.js";
|
|
8
|
-
import { HyperliquidAdapter } from "../exchanges/hyperliquid.js";
|
|
10
|
+
import { HyperliquidAdapter, hlAbstractionCachePrefix } from "../exchanges/hyperliquid.js";
|
|
11
|
+
import { invalidateCache } from "../cache.js";
|
|
12
|
+
import { LighterAdapter } from "../exchanges/lighter.js";
|
|
13
|
+
import { resolveVenueId } from "../exchanges/venue.js";
|
|
9
14
|
export function registerWalletManageCommands(parent, getAdapter, isJson, getPacificaAdapter) {
|
|
10
|
-
const manage = parent.command("manage").description("Exchange account settings (margin, subaccount, API keys, builder, account-mode) — Pacifica + Lighter + Hyperliquid");
|
|
15
|
+
const manage = parent.command("manage").description("Exchange account settings (margin, subaccount, API keys, builder-owner tools, account-mode) — Pacifica + Lighter + Hyperliquid");
|
|
11
16
|
// Ensure adapter is initialized before accessing PacificaAdapter
|
|
12
17
|
async function pac() {
|
|
13
18
|
const adapter = await getAdapter();
|
|
@@ -45,15 +50,15 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
45
50
|
withdrawCmd._hidden = true;
|
|
46
51
|
// Subaccounts
|
|
47
52
|
const sub = manage.command("sub").description("Subaccount management");
|
|
53
|
+
// Gated. Docs (rest-api/subaccounts/create-subaccount): "Both the main account and subaccount
|
|
54
|
+
// must sign to establish the relationship." perp-cli has no keypair source for the subaccount
|
|
55
|
+
// signature, so the request cannot be built.
|
|
48
56
|
sub
|
|
49
57
|
.command("create <name>")
|
|
50
|
-
.description("Create a subaccount")
|
|
51
|
-
.action(async (
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (isJson())
|
|
55
|
-
return printJson(jsonOk(result));
|
|
56
|
-
console.log(chalk.green(`\n Subaccount "${name}" created.\n`));
|
|
58
|
+
.description("Create a subaccount (not supported: the subaccount wallet must co-sign — use the Pacifica web app)")
|
|
59
|
+
.action(async (_name) => {
|
|
60
|
+
await pac();
|
|
61
|
+
throw unsupported("Pacifica", "subaccountCreate", "creating a subaccount needs a signature from the subaccount wallet as well as the main account, and perp-cli only holds the main account signer", "Create the subaccount in the Pacifica web app, then use: perp wallet manage sub list / sub transfer");
|
|
57
62
|
});
|
|
58
63
|
sub
|
|
59
64
|
.command("list")
|
|
@@ -67,10 +72,17 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
67
72
|
});
|
|
68
73
|
sub
|
|
69
74
|
.command("transfer <from> <to> <amount>")
|
|
70
|
-
.description("Transfer
|
|
75
|
+
.description("Transfer USDC between a main account and its subaccounts (<from> must be the signing account)")
|
|
71
76
|
.action(async (from, to, amount) => {
|
|
72
77
|
const a = await pac();
|
|
73
|
-
|
|
78
|
+
// Docs (subaccount-fund-transfer): the sender is the signing "account"; there is no from field.
|
|
79
|
+
if (from !== a.publicKey) {
|
|
80
|
+
throw new PerpError("INVALID_PARAMS", `Pacifica transfers are sent by the signing account (${a.publicKey}); <from> was ${from}`, {
|
|
81
|
+
exchange: "pacifica",
|
|
82
|
+
remediation: "Pass the active account as <from>, or run the command with the sender's wallet",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const result = await a.sdk.transferFunds({ to_account: to, amount: parsePositiveDecimal(amount, "transfer amount") }, a.publicKey, a.signer);
|
|
74
86
|
if (isJson())
|
|
75
87
|
return printJson(jsonOk(result));
|
|
76
88
|
console.log(chalk.green(`\n Transferred $${amount}.\n`));
|
|
@@ -81,77 +93,48 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
81
93
|
// and OWS at-rest encryption. See `wallet agent approve pacifica --help`.
|
|
82
94
|
// Lake (liquidity vaults)
|
|
83
95
|
const lake = manage.command("lake").description("Lake (liquidity vault) management");
|
|
96
|
+
// Field names per docs.pacifica.fi rest-api/vaults (create-vault / deposit-to-vault / withdraw-from-vault).
|
|
84
97
|
lake
|
|
85
|
-
.command("create
|
|
86
|
-
.description("Create a new lake")
|
|
87
|
-
.
|
|
98
|
+
.command("create")
|
|
99
|
+
.description("Create a new vault (lake)")
|
|
100
|
+
.option("--manager <address>", "Manager address (omit to create a fresh vault with no manager)")
|
|
101
|
+
.option("--nickname <name>", "Display name for the vault")
|
|
102
|
+
.option("--initial-deposit <amount>", "Seed deposit in USDC from the creator")
|
|
103
|
+
.action(async (opts) => {
|
|
88
104
|
const a = await pac();
|
|
89
|
-
const result = await a.sdk.createLake({
|
|
105
|
+
const result = await a.sdk.createLake({
|
|
106
|
+
manager: opts.manager === undefined ? null : opts.manager,
|
|
107
|
+
nickname: opts.nickname === undefined ? null : opts.nickname,
|
|
108
|
+
initial_deposit: opts.initialDeposit === undefined ? null : parsePositiveDecimal(opts.initialDeposit, "--initial-deposit"),
|
|
109
|
+
}, a.publicKey, a.signer);
|
|
90
110
|
if (isJson())
|
|
91
111
|
return printJson(jsonOk(result));
|
|
92
|
-
console.log(chalk.green(`\n
|
|
112
|
+
console.log(chalk.green(`\n Vault created.\n`));
|
|
113
|
+
console.log(JSON.stringify(result, null, 2));
|
|
93
114
|
});
|
|
94
115
|
lake
|
|
95
|
-
.command("deposit <
|
|
96
|
-
.description("Deposit
|
|
97
|
-
.action(async (
|
|
116
|
+
.command("deposit <lake> <amount>")
|
|
117
|
+
.description("Deposit USDC into a vault (<lake> = vault address)")
|
|
118
|
+
.action(async (lakeAddress, amount) => {
|
|
98
119
|
const a = await pac();
|
|
99
|
-
const result = await a.sdk.depositToLake({
|
|
120
|
+
const result = await a.sdk.depositToLake({ lake: lakeAddress, amount: parsePositiveDecimal(amount, "deposit amount") }, a.publicKey, a.signer);
|
|
100
121
|
if (isJson())
|
|
101
122
|
return printJson(jsonOk(result));
|
|
102
|
-
console.log(chalk.green(`\n Deposited $${amount} to
|
|
123
|
+
console.log(chalk.green(`\n Deposited $${amount} to vault ${lakeAddress}.\n`));
|
|
103
124
|
});
|
|
104
125
|
lake
|
|
105
|
-
.command("withdraw <
|
|
106
|
-
.description("
|
|
107
|
-
.action(async (
|
|
108
|
-
const a = await pac();
|
|
109
|
-
const result = await a.sdk.withdrawFromLake({ lake_id: lakeId, amount }, a.publicKey, a.signer);
|
|
110
|
-
if (isJson())
|
|
111
|
-
return printJson(jsonOk(result));
|
|
112
|
-
console.log(chalk.green(`\n Withdrew $${amount} from lake ${lakeId}.\n`));
|
|
113
|
-
});
|
|
114
|
-
// Builder Codes
|
|
115
|
-
const builder = manage.command("builder").description("Builder code management (Pacifica)");
|
|
116
|
-
builder
|
|
117
|
-
.command("approve <code> <maxFeeRate>")
|
|
118
|
-
.description("Approve a builder code (e.g. approve MYCODE 0.001)")
|
|
119
|
-
.action(async (code, maxFeeRate) => {
|
|
126
|
+
.command("withdraw <lake> <shares>")
|
|
127
|
+
.description("Redeem vault shares (<lake> = vault address, <shares> = number of shares, not USDC)")
|
|
128
|
+
.action(async (lakeAddress, shares) => {
|
|
120
129
|
const a = await pac();
|
|
121
|
-
const result = await a.sdk.
|
|
130
|
+
const result = await a.sdk.withdrawFromLake({ lake: lakeAddress, shares: parsePositiveDecimal(shares, "shares") }, a.publicKey, a.signer);
|
|
122
131
|
if (isJson())
|
|
123
132
|
return printJson(jsonOk(result));
|
|
124
|
-
console.log(chalk.green(`\n
|
|
125
|
-
});
|
|
126
|
-
builder
|
|
127
|
-
.command("revoke <code>")
|
|
128
|
-
.description("Revoke a builder code")
|
|
129
|
-
.action(async (code) => {
|
|
130
|
-
const a = await pac();
|
|
131
|
-
const result = await a.sdk.revokeBuilderCode({ builder_code: code }, a.publicKey, a.signer);
|
|
132
|
-
if (isJson())
|
|
133
|
-
return printJson(jsonOk(result));
|
|
134
|
-
console.log(chalk.green(`\n Builder code "${code}" revoked.\n`));
|
|
135
|
-
});
|
|
136
|
-
builder
|
|
137
|
-
.command("list")
|
|
138
|
-
.description("List approved builder codes")
|
|
139
|
-
.action(async () => {
|
|
140
|
-
const a = await pac();
|
|
141
|
-
const result = await a.sdk.getBuilderApprovals(a.publicKey);
|
|
142
|
-
if (isJson())
|
|
143
|
-
return printJson(jsonOk(result));
|
|
144
|
-
const approvals = result;
|
|
145
|
-
if (!Array.isArray(approvals) || approvals.length === 0) {
|
|
146
|
-
console.log(chalk.gray("\n No builder codes approved.\n"));
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
console.log(chalk.cyan.bold("\n Approved Builder Codes\n"));
|
|
150
|
-
for (const b of approvals) {
|
|
151
|
-
console.log(` ${chalk.white(b.builder_code.padEnd(16))} max_fee: ${b.max_fee_rate} ${chalk.gray(b.description || "")}`);
|
|
152
|
-
}
|
|
153
|
-
console.log();
|
|
133
|
+
console.log(chalk.green(`\n Redeemed ${shares} shares from vault ${lakeAddress}.\n`));
|
|
154
134
|
});
|
|
135
|
+
// Pacifica builder-OWNER tools. A user's builder approval / revoke / status is
|
|
136
|
+
// `perp wallet builder approve|revoke|status <venue>` (settings builder.<venue>).
|
|
137
|
+
const builder = manage.command("builder").description("Pacifica builder-owner tools (overview, trades, leaderboard, update-fee). Approve / revoke / status: perp wallet builder");
|
|
155
138
|
builder
|
|
156
139
|
.command("overview")
|
|
157
140
|
.description("Show your builder code overview (if you are a builder)")
|
|
@@ -193,7 +176,7 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
193
176
|
console.log(chalk.green(`\n Builder code "${code}" fee rate updated to ${feeRate}.\n`));
|
|
194
177
|
});
|
|
195
178
|
// Referral
|
|
196
|
-
const referral = manage.command("referral").description("Referral
|
|
179
|
+
const referral = manage.command("referral").description("Referral codes (Pacifica claim; Hyperliquid/Lighter explicit set)");
|
|
197
180
|
referral
|
|
198
181
|
.command("claim <code>")
|
|
199
182
|
.description("Claim a referral code")
|
|
@@ -204,17 +187,74 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
204
187
|
return printJson(jsonOk(result));
|
|
205
188
|
console.log(chalk.green(`\n Referral code "${code}" claimed!\n`));
|
|
206
189
|
});
|
|
190
|
+
// Explicit referral apply (HL / Lighter). Never done on adapter creation: this
|
|
191
|
+
// signs and sends one referral action with the active signer. Supports --dry-run.
|
|
192
|
+
supportsDryRun(referral
|
|
193
|
+
.command("set [code]")
|
|
194
|
+
.description("Hyperliquid / Lighter: sign + send a one-time referral action (default code: settings referralCodes.<venue>; supports --dry-run)")
|
|
195
|
+
.action(async (codeArg, _opts, cmd) => {
|
|
196
|
+
await withJsonErrors(isJson(), async () => {
|
|
197
|
+
const venue = resolveVenueId(String(cmd.optsWithGlobals().exchange));
|
|
198
|
+
if (venue !== "hyperliquid" && venue !== "lighter") {
|
|
199
|
+
throw new PerpError("CAPABILITY_UNSUPPORTED", `referral set supports hyperliquid and lighter (got ${venue})`, {
|
|
200
|
+
remediation: "Run with -e hyperliquid or -e lighter (Pacifica: perp -e pacifica wallet manage referral claim <code>)",
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
const code = codeArg ?? loadSettings().referralCodes[venue];
|
|
204
|
+
if (!code) {
|
|
205
|
+
throw new PerpError("INVALID_PARAMS", `No ${venue} referral code given and settings referralCodes.${venue} is empty`, {
|
|
206
|
+
remediation: `Pass a code: perp -e ${venue} wallet manage referral set <code>`,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
const network = networkOf(cmd);
|
|
210
|
+
const plan = { venue, network, code, action: venue === "hyperliquid" ? "setReferrer" : "referral/use" };
|
|
211
|
+
if (isDryRun(cmd)) {
|
|
212
|
+
if (isJson())
|
|
213
|
+
return printJson(jsonOk({ dryRun: true, sent: false, ...plan }));
|
|
214
|
+
console.log(chalk.yellow(`\n [dry-run] Would sign + send ${plan.action} with code "${code}" on ${venue} (${network}). Nothing sent.\n`));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// The referral is the account owner's action — never signed with an imported agent / API key.
|
|
218
|
+
const { cliCredentialOptsFrom, requireMasterWallet } = await import("../config.js");
|
|
219
|
+
await requireMasterWallet(venue, cliCredentialOptsFrom(cmd.optsWithGlobals()), `${venue} referral set`);
|
|
220
|
+
const adapter = await getAdapter();
|
|
221
|
+
let result;
|
|
222
|
+
if (venue === "hyperliquid") {
|
|
223
|
+
if (!(adapter instanceof HyperliquidAdapter))
|
|
224
|
+
throw new Error("hyperliquid venue did not produce a HyperliquidAdapter");
|
|
225
|
+
result = await adapter.setReferrer(code);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
if (!(adapter instanceof LighterAdapter))
|
|
229
|
+
throw new Error("lighter venue did not produce a LighterAdapter");
|
|
230
|
+
if (adapter.isReadOnly) {
|
|
231
|
+
throw new PerpError("NO_SIGNER_AVAILABLE", "Lighter referral needs a signer (agent API key)", {
|
|
232
|
+
remediation: "Register one first: perp wallet agent approve lighter",
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
result = await adapter.useReferralCode(code);
|
|
236
|
+
}
|
|
237
|
+
const s = loadSettings();
|
|
238
|
+
s.referralApplied[venue] = true;
|
|
239
|
+
saveSettings(s);
|
|
240
|
+
if (isJson())
|
|
241
|
+
return printJson(jsonOk({ sent: true, ...plan, result }));
|
|
242
|
+
console.log(chalk.green(`\n ${venue} referral code "${code}" applied.\n`));
|
|
243
|
+
});
|
|
244
|
+
}));
|
|
207
245
|
// API Keys
|
|
208
246
|
const apikey = manage.command("apikey").description("API key management");
|
|
209
247
|
apikey
|
|
210
|
-
.command("create
|
|
211
|
-
.description("Create an API key")
|
|
212
|
-
.action(async (
|
|
248
|
+
.command("create")
|
|
249
|
+
.description("Create an API Config Key (rate-limit key; up to 5 per account)")
|
|
250
|
+
.action(async () => {
|
|
213
251
|
const a = await pac();
|
|
214
|
-
|
|
252
|
+
// Docs (api/rate-limits/api-config-keys): no name / fee-rate field — the signed payload is empty.
|
|
253
|
+
const result = await a.sdk.createApiKey(a.publicKey, a.signer);
|
|
215
254
|
if (isJson())
|
|
216
255
|
return printJson(jsonOk(result));
|
|
217
|
-
console.log(chalk.green(`\n API
|
|
256
|
+
console.log(chalk.green(`\n API Config Key created.\n`));
|
|
257
|
+
console.log(JSON.stringify(result, null, 2));
|
|
218
258
|
});
|
|
219
259
|
apikey
|
|
220
260
|
.command("list")
|
|
@@ -258,7 +298,7 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
258
298
|
// Merge parent + subcommand opts so --passphrase works on either side.
|
|
259
299
|
const mergedOpts = command.optsWithGlobals();
|
|
260
300
|
const passphraseFlag = mergedOpts.passphrase ?? opts.passphrase;
|
|
261
|
-
const network =
|
|
301
|
+
const network = networkOf(command);
|
|
262
302
|
const isTestnet = network === "testnet";
|
|
263
303
|
// ── Show branch (no positional arg) ─────────────────────────────────
|
|
264
304
|
if (!mode) {
|
|
@@ -285,7 +325,8 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
285
325
|
});
|
|
286
326
|
}
|
|
287
327
|
adapter.setAddress(evmAccount.address);
|
|
288
|
-
|
|
328
|
+
// The command that reports the mode never answers from the disk cache.
|
|
329
|
+
const current = await adapter._getAbstractionMode({ fresh: true });
|
|
289
330
|
if (useJson) {
|
|
290
331
|
return printJson(jsonOk({ mode: current, master: masterName, address: evmAccount.address, network: isTestnet ? "testnet" : "mainnet" }));
|
|
291
332
|
}
|
|
@@ -298,9 +339,7 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
298
339
|
}
|
|
299
340
|
catch (err) {
|
|
300
341
|
if (useJson) {
|
|
301
|
-
const e = err
|
|
302
|
-
? err.structured
|
|
303
|
-
: { code: "UNKNOWN", message: err instanceof Error ? err.message : String(err) };
|
|
342
|
+
const e = classifyError(err);
|
|
304
343
|
console.error(JSON.stringify(jsonError(e.code, e.message)));
|
|
305
344
|
process.exit(1);
|
|
306
345
|
}
|
|
@@ -345,7 +384,7 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
345
384
|
const evmAccount = wallet.accounts.find((a) => a.chainId.startsWith("eip155:"));
|
|
346
385
|
if (evmAccount) {
|
|
347
386
|
readAdapter.setAddress(evmAccount.address);
|
|
348
|
-
prevMode = await readAdapter._getAbstractionMode();
|
|
387
|
+
prevMode = await readAdapter._getAbstractionMode({ fresh: true });
|
|
349
388
|
}
|
|
350
389
|
}
|
|
351
390
|
catch {
|
|
@@ -354,12 +393,20 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
354
393
|
// informational metadata — its absence does NOT mask the write.
|
|
355
394
|
prevMode = null;
|
|
356
395
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
396
|
+
// The cached mode (HL_ABSTRACTION_TTL_MS) is dropped whatever the outcome: a sent action
|
|
397
|
+
// whose answer was lost may have changed the mode.
|
|
398
|
+
let result;
|
|
399
|
+
try {
|
|
400
|
+
result = await runHlSetAbstractionFlow({
|
|
401
|
+
masterName,
|
|
402
|
+
passphrase: passphrase ?? "",
|
|
403
|
+
mode: newMode,
|
|
404
|
+
isTestnet,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
finally {
|
|
408
|
+
invalidateCache(hlAbstractionCachePrefix(network));
|
|
409
|
+
}
|
|
363
410
|
if (useJson) {
|
|
364
411
|
return printJson(jsonOk({
|
|
365
412
|
mode: result.mode,
|
|
@@ -380,9 +427,7 @@ export function registerWalletManageCommands(parent, getAdapter, isJson, getPaci
|
|
|
380
427
|
}
|
|
381
428
|
catch (err) {
|
|
382
429
|
if (useJson) {
|
|
383
|
-
const e = err
|
|
384
|
-
? err.structured
|
|
385
|
-
: { code: "UNKNOWN", message: err instanceof Error ? err.message : String(err) };
|
|
430
|
+
const e = classifyError(err);
|
|
386
431
|
console.error(JSON.stringify(jsonError(e.code, e.message)));
|
|
387
432
|
process.exit(1);
|
|
388
433
|
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
3
|
-
|
|
3
|
+
import "../exchanges/venues/index.js";
|
|
4
|
+
export declare function registerMarketCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>,
|
|
5
|
+
/** Credentialed adapter of the selected venue — for market data the venue serves only to an authenticated account. */
|
|
6
|
+
getAccountAdapter?: () => Promise<ExchangeAdapter>): void;
|
package/dist/commands/market.js
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { makeTable, formatUsd, formatPercent, printJson, jsonOk, jsonError, withJsonErrors } from "../utils.js";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { PerpError } from "../errors.js";
|
|
4
|
+
import { midDecimal } from "../exchanges/precision.js";
|
|
5
|
+
import { hasPacificaSdk, isDexCapable, klinesNeedAuth } from "../exchanges/capabilities.js";
|
|
6
|
+
import { listVenuesWithRole } from "../exchanges/venue.js";
|
|
7
|
+
import "../exchanges/venues/index.js";
|
|
8
|
+
/** Venues with an adapter (market-data-only venues have none — see `perp arb scan`). */
|
|
9
|
+
const EXCHANGES = listVenuesWithRole("account").map(v => v.id);
|
|
10
|
+
/** null = venue does not publish the value → "-" (never rendered as $0). */
|
|
11
|
+
const usdOrDash = (v) => (v === null ? chalk.gray("-") : `$${formatUsd(v)}`);
|
|
12
|
+
export function registerMarketCommands(program, getAdapter, isJson, getAdapterForExchange,
|
|
13
|
+
/** Credentialed adapter of the selected venue — for market data the venue serves only to an authenticated account. */
|
|
14
|
+
getAccountAdapter) {
|
|
6
15
|
const market = program.command("market").description("Market data commands");
|
|
7
16
|
market
|
|
8
17
|
.command("list")
|
|
@@ -14,10 +23,10 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
14
23
|
const marketRow = (m) => [
|
|
15
24
|
chalk.white.bold(m.symbol),
|
|
16
25
|
`$${formatUsd(m.markPrice)}`,
|
|
17
|
-
|
|
26
|
+
usdOrDash(m.indexPrice),
|
|
18
27
|
m.fundingRate != null ? formatPercent(m.fundingRate) : chalk.gray("N/A"),
|
|
19
|
-
|
|
20
|
-
|
|
28
|
+
usdOrDash(m.volume24h),
|
|
29
|
+
usdOrDash(m.openInterest),
|
|
21
30
|
String(m.maxLeverage) + "x",
|
|
22
31
|
];
|
|
23
32
|
// Multi-exchange mode
|
|
@@ -97,7 +106,7 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
97
106
|
const priceRow = (m) => [
|
|
98
107
|
chalk.white.bold(m.symbol),
|
|
99
108
|
`$${formatUsd(m.markPrice)}`,
|
|
100
|
-
|
|
109
|
+
usdOrDash(m.indexPrice),
|
|
101
110
|
m.fundingRate != null ? formatPercent(m.fundingRate) : chalk.gray("N/A"),
|
|
102
111
|
];
|
|
103
112
|
if (!explicitExchange && getAdapterForExchange) {
|
|
@@ -169,10 +178,10 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
169
178
|
return printJson(jsonOk(m));
|
|
170
179
|
console.log(chalk.cyan.bold(`\n ${m.symbol} on ${adapter.name}\n`));
|
|
171
180
|
console.log(` Mark Price: $${formatUsd(m.markPrice)}`);
|
|
172
|
-
console.log(` Index Price:
|
|
181
|
+
console.log(` Index Price: ${usdOrDash(m.indexPrice)}`);
|
|
173
182
|
console.log(` Funding Rate: ${m.fundingRate != null ? formatPercent(m.fundingRate) : chalk.gray("N/A")}`);
|
|
174
|
-
console.log(` 24h Volume:
|
|
175
|
-
console.log(` Open Interest:
|
|
183
|
+
console.log(` 24h Volume: ${usdOrDash(m.volume24h)}`);
|
|
184
|
+
console.log(` Open Interest: ${usdOrDash(m.openInterest)}`);
|
|
176
185
|
console.log(` Max Leverage: ${m.maxLeverage}x`);
|
|
177
186
|
console.log();
|
|
178
187
|
});
|
|
@@ -279,17 +288,19 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
279
288
|
.description("Get mid price for a single symbol (fast)")
|
|
280
289
|
.action(async (symbol) => {
|
|
281
290
|
const sym = symbol.toUpperCase();
|
|
282
|
-
|
|
291
|
+
// withJsonErrors: every failure (incl. an empty book) is an error envelope AND exit code 1.
|
|
292
|
+
await withJsonErrors(isJson(), async () => {
|
|
283
293
|
const adapter = await getAdapter();
|
|
284
|
-
//
|
|
294
|
+
// Orderbook mid (best bid + best ask) / 2
|
|
285
295
|
const book = await adapter.getOrderbook(sym);
|
|
286
296
|
const bestBid = book.bids[0]?.[0];
|
|
287
297
|
const bestAsk = book.asks[0]?.[0];
|
|
288
298
|
if (!bestBid && !bestAsk) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
299
|
+
// The market exists (getOrderbook resolved it) but nobody quotes it: no price, not "unknown symbol".
|
|
300
|
+
throw new PerpError("PRICE_STALE", `${sym} orderbook on ${adapter.name} is empty (no bids and no asks) — no mid price`, {
|
|
301
|
+
exchange: adapter.name,
|
|
302
|
+
remediation: `The market has no liquidity right now; check: perp -e ${adapter.name} market book ${sym}, or pick a market with a two-sided book`,
|
|
303
|
+
});
|
|
293
304
|
}
|
|
294
305
|
const bid = Number(bestBid ?? 0);
|
|
295
306
|
const ask = Number(bestAsk ?? 0);
|
|
@@ -298,7 +309,8 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
298
309
|
if (isJson())
|
|
299
310
|
return printJson(jsonOk({
|
|
300
311
|
symbol: sym,
|
|
301
|
-
mid
|
|
312
|
+
// Exact decimal mid of the two quoted strings (float (bid + ask) / 2 prints artifacts).
|
|
313
|
+
mid: bestBid && bestAsk ? midDecimal(bestBid, bestAsk) : mid.toString(),
|
|
302
314
|
bid: bestBid ?? null,
|
|
303
315
|
ask: bestAsk ?? null,
|
|
304
316
|
spread: spread.toFixed(6),
|
|
@@ -311,19 +323,7 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
311
323
|
console.log(` Ask: $${formatUsd(bestAsk)}`);
|
|
312
324
|
console.log(` Spread: ${spread.toFixed(4)}%`);
|
|
313
325
|
console.log();
|
|
314
|
-
}
|
|
315
|
-
catch (err) {
|
|
316
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
317
|
-
if (isJson()) {
|
|
318
|
-
const { classifyError } = await import("../errors.js");
|
|
319
|
-
const classified = classifyError(err);
|
|
320
|
-
return printJson(jsonError(classified.code, classified.message, {
|
|
321
|
-
status: classified.status,
|
|
322
|
-
retryable: classified.retryable,
|
|
323
|
-
}));
|
|
324
|
-
}
|
|
325
|
-
console.error(chalk.red(`Error: ${msg}`));
|
|
326
|
-
}
|
|
326
|
+
});
|
|
327
327
|
});
|
|
328
328
|
market
|
|
329
329
|
.command("kline <symbol> <interval>")
|
|
@@ -331,7 +331,9 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
331
331
|
.option("--start <ts>", "Start time (unix ms)")
|
|
332
332
|
.option("--end <ts>", "End time (unix ms)")
|
|
333
333
|
.action(async (symbol, interval, opts) => {
|
|
334
|
-
const
|
|
334
|
+
const publicAdapter = await getAdapter();
|
|
335
|
+
// A venue whose candles endpoint is authenticated (Ondo) is read with the registered key.
|
|
336
|
+
const adapter = getAccountAdapter !== undefined && klinesNeedAuth(publicAdapter) ? await getAccountAdapter() : publicAdapter;
|
|
335
337
|
const now = Date.now();
|
|
336
338
|
const startTime = opts.start ? parseInt(opts.start) : now - 24 * 60 * 60 * 1000;
|
|
337
339
|
const endTime = opts.end ? parseInt(opts.end) : now;
|
|
@@ -362,8 +364,9 @@ export function registerMarketCommands(program, getAdapter, isJson, getAdapterFo
|
|
|
362
364
|
const adapter = await getAdapter();
|
|
363
365
|
if (!(isDexCapable(adapter))) {
|
|
364
366
|
if (isJson())
|
|
365
|
-
return printJson(jsonError("
|
|
367
|
+
return printJson(jsonError("INVALID_PARAMS", "HIP-3 dexes are only available on Hyperliquid.", { remediation: "Re-run with -e hyperliquid." }));
|
|
366
368
|
console.error(chalk.red("\n HIP-3 dexes are only available on Hyperliquid. Use -e hyperliquid.\n"));
|
|
369
|
+
process.exitCode = 1;
|
|
367
370
|
return;
|
|
368
371
|
}
|
|
369
372
|
const dexes = await adapter.listDeployedDexes();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `perp outcome` — Hyperliquid Outcome markets (HIP-4).
|
|
3
|
+
*
|
|
4
|
+
* Currently HL-only. Asset class is fully collateralized binary/range
|
|
5
|
+
* contracts, quoted per outcome (outcomeMeta quoteToken, e.g. USDC). No leverage, no liquidation. Settlement is
|
|
6
|
+
* dated per-outcome.
|
|
7
|
+
*/
|
|
8
|
+
import { Command } from "commander";
|
|
9
|
+
import type { ExchangeAdapter } from "../exchanges/index.js";
|
|
10
|
+
export declare function registerOutcomeCommands(program: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean): void;
|