perp-cli 0.9.8 → 0.12.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 +30 -25
- package/dist/_stderr-filter.d.ts +0 -0
- package/dist/_stderr-filter.js +30 -0
- package/dist/agent-wallet/expiry.d.ts +15 -0
- package/dist/agent-wallet/expiry.js +26 -0
- package/dist/agent-wallet/lighter-keystore.d.ts +34 -0
- package/dist/agent-wallet/lighter-keystore.js +232 -0
- package/dist/agent-wallet/passphrase.d.ts +13 -0
- package/dist/agent-wallet/passphrase.js +54 -0
- package/dist/agent-wallet/signing-strategy.d.ts +67 -0
- package/dist/agent-wallet/signing-strategy.js +51 -0
- package/dist/agent-wallet/store.d.ts +37 -0
- package/dist/agent-wallet/store.js +172 -0
- package/dist/api/public/_http.d.ts +15 -0
- package/dist/api/public/_http.js +24 -0
- package/dist/api/public/hyperliquid.js +36 -24
- package/dist/api/public/lighter.d.ts +7 -1
- package/dist/api/public/lighter.js +85 -37
- package/dist/api/public/pacifica.js +47 -18
- package/dist/bot/engine.js +23 -4
- package/dist/bot/presets.js +3 -3
- package/dist/bot/strategies/funding-arb-strategy.d.ts +0 -23
- package/dist/bot/strategies/funding-arb-strategy.js +41 -176
- package/dist/bot/strategies/funding-auto.js +2 -2
- package/dist/bridge-engine.js +102 -49
- package/dist/cli-spec.js +1 -2
- package/dist/commands/account.js +2 -500
- package/dist/commands/agent.d.ts +8 -2
- package/dist/commands/agent.js +1903 -366
- package/dist/commands/alerts.js +3 -1
- package/dist/commands/arb/index.js +37 -7
- package/dist/commands/arb-auto.js +44 -11
- package/dist/commands/arb.js +83 -34
- package/dist/commands/bot.d.ts +1 -1
- package/dist/commands/bot.js +20 -16
- package/dist/commands/bridge.d.ts +1 -1
- package/dist/commands/bridge.js +4 -4
- package/dist/commands/funds.d.ts +1 -1
- package/dist/commands/funds.js +18 -68
- package/dist/commands/jobs.d.ts +1 -1
- package/dist/commands/jobs.js +2 -2
- package/dist/commands/manage.d.ts +1 -1
- package/dist/commands/manage.js +12 -84
- package/dist/commands/plan.d.ts +1 -1
- package/dist/commands/plan.js +2 -2
- package/dist/commands/portfolio.d.ts +19 -3
- package/dist/commands/portfolio.js +747 -163
- package/dist/commands/rebalance.d.ts +1 -1
- package/dist/commands/rebalance.js +18 -12
- package/dist/commands/risk.js +2 -6
- package/dist/commands/settings.js +30 -53
- package/dist/commands/trade.js +16 -15
- package/dist/commands/wallet.d.ts +2 -1
- package/dist/commands/wallet.js +1122 -176
- package/dist/config.d.ts +9 -0
- package/dist/config.js +55 -9
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +82 -2
- package/dist/event-stream.js +12 -1
- package/dist/exchanges/aster-typed-data.d.ts +156 -0
- package/dist/exchanges/aster-typed-data.js +189 -0
- package/dist/exchanges/aster.d.ts +60 -11
- package/dist/exchanges/aster.js +278 -136
- package/dist/exchanges/hyperliquid.d.ts +14 -0
- package/dist/exchanges/hyperliquid.js +88 -34
- package/dist/exchanges/lighter-spot.js +19 -5
- package/dist/exchanges/lighter.d.ts +66 -0
- package/dist/exchanges/lighter.js +213 -31
- package/dist/exchanges/pacifica-typed-data.d.ts +75 -0
- package/dist/exchanges/pacifica-typed-data.js +74 -0
- package/dist/exchanges/pacifica.d.ts +29 -1
- package/dist/exchanges/pacifica.js +94 -6
- package/dist/funding/rates.js +59 -21
- package/dist/guardrail/contracts.d.ts +14 -0
- package/dist/guardrail/contracts.js +72 -0
- package/dist/guardrail/perp-guardrail.d.ts +21 -0
- package/dist/guardrail/perp-guardrail.js +145 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +307 -362
- package/dist/jobs.js +3 -1
- package/dist/mcp-server.js +92 -88
- package/dist/position-history.js +47 -4
- package/dist/settings.d.ts +69 -3
- package/dist/settings.js +3 -2
- package/dist/signer/index.d.ts +2 -0
- package/dist/signer/index.js +2 -0
- package/dist/signer/ows-evm.d.ts +32 -0
- package/dist/signer/ows-evm.js +86 -0
- package/dist/signer/ows-loader.d.ts +2 -0
- package/dist/signer/ows-loader.js +6 -0
- package/dist/signer/ows-solana.d.ts +25 -0
- package/dist/signer/ows-solana.js +56 -0
- package/dist/smart-order.d.ts +8 -2
- package/dist/smart-order.js +46 -11
- package/dist/strategies/funding-arb.js +29 -8
- package/dist/trade-validator.js +30 -5
- package/dist/utils.d.ts +10 -0
- package/dist/utils.js +19 -0
- package/package.json +4 -2
- package/skills/perp-cli/SKILL.md +23 -14
- package/skills/perp-cli/references/agent-operations.md +6 -4
- package/skills/perp-cli/references/commands.md +55 -34
- package/skills/perp-cli/scripts/preflight.sh +4 -4
- package/skills/perp-cli/scripts/spot-perp-scan.sh +2 -2
- package/dist/__tests__/alert-logic.test.d.ts +0 -1
- package/dist/__tests__/alert-logic.test.js +0 -107
- package/dist/__tests__/arb-auto-3dex.test.d.ts +0 -1
- package/dist/__tests__/arb-auto-3dex.test.js +0 -397
- package/dist/__tests__/arb-history-stats.test.d.ts +0 -1
- package/dist/__tests__/arb-history-stats.test.js +0 -176
- package/dist/__tests__/arb-logic.test.d.ts +0 -1
- package/dist/__tests__/arb-logic.test.js +0 -84
- package/dist/__tests__/arb-manage.test.d.ts +0 -1
- package/dist/__tests__/arb-manage.test.js +0 -253
- package/dist/__tests__/arb-new-features.test.d.ts +0 -1
- package/dist/__tests__/arb-new-features.test.js +0 -457
- package/dist/__tests__/arb-sizing.test.d.ts +0 -1
- package/dist/__tests__/arb-sizing.test.js +0 -48
- package/dist/__tests__/arb-state.test.d.ts +0 -1
- package/dist/__tests__/arb-state.test.js +0 -284
- package/dist/__tests__/arb-userflow.test.d.ts +0 -1
- package/dist/__tests__/arb-userflow.test.js +0 -945
- package/dist/__tests__/arb-utils.test.d.ts +0 -1
- package/dist/__tests__/arb-utils.test.js +0 -264
- package/dist/__tests__/bot-conditions.test.d.ts +0 -1
- package/dist/__tests__/bot-conditions.test.js +0 -341
- package/dist/__tests__/client-id-tracker.test.d.ts +0 -1
- package/dist/__tests__/client-id-tracker.test.js +0 -137
- package/dist/__tests__/commands/new-atomic-commands.test.d.ts +0 -1
- package/dist/__tests__/commands/new-atomic-commands.test.js +0 -502
- package/dist/__tests__/commands/order-intent.test.d.ts +0 -1
- package/dist/__tests__/commands/order-intent.test.js +0 -600
- package/dist/__tests__/commands/trade-commands.test.d.ts +0 -1
- package/dist/__tests__/commands/trade-commands.test.js +0 -821
- package/dist/__tests__/config.test.d.ts +0 -1
- package/dist/__tests__/config.test.js +0 -86
- package/dist/__tests__/cross-chain-margin.test.d.ts +0 -1
- package/dist/__tests__/cross-chain-margin.test.js +0 -287
- package/dist/__tests__/dex-asset-map.test.d.ts +0 -1
- package/dist/__tests__/dex-asset-map.test.js +0 -191
- package/dist/__tests__/errors.test.d.ts +0 -1
- package/dist/__tests__/errors.test.js +0 -110
- package/dist/__tests__/event-stream.test.d.ts +0 -1
- package/dist/__tests__/event-stream.test.js +0 -276
- package/dist/__tests__/exchanges/interface.test.d.ts +0 -1
- package/dist/__tests__/exchanges/interface.test.js +0 -132
- package/dist/__tests__/exchanges/mock-adapter.d.ts +0 -69
- package/dist/__tests__/exchanges/mock-adapter.js +0 -137
- package/dist/__tests__/execution-log.test.d.ts +0 -1
- package/dist/__tests__/execution-log.test.js +0 -106
- package/dist/__tests__/funding-calc.test.d.ts +0 -1
- package/dist/__tests__/funding-calc.test.js +0 -71
- package/dist/__tests__/funding-history.test.d.ts +0 -1
- package/dist/__tests__/funding-history.test.js +0 -343
- package/dist/__tests__/funding-rates.test.d.ts +0 -1
- package/dist/__tests__/funding-rates.test.js +0 -342
- package/dist/__tests__/funding.test.d.ts +0 -1
- package/dist/__tests__/funding.test.js +0 -173
- package/dist/__tests__/gap-logic.test.d.ts +0 -1
- package/dist/__tests__/gap-logic.test.js +0 -43
- package/dist/__tests__/hip3-dex.test.d.ts +0 -1
- package/dist/__tests__/hip3-dex.test.js +0 -234
- package/dist/__tests__/integration/agent-features.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/agent-features.integration.test.js +0 -553
- package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +0 -13
- package/dist/__tests__/integration/atomic-commands.integration.test.js +0 -246
- package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/bridge-simulation.integration.test.js +0 -453
- package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/bridge-strict.integration.test.js +0 -812
- package/dist/__tests__/integration/bridge.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/bridge.integration.test.js +0 -309
- package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/cli-e2e.integration.test.js +0 -202
- package/dist/__tests__/integration/dex-arb.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/dex-arb.integration.test.js +0 -116
- package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +0 -13
- package/dist/__tests__/integration/envelope-consistency.integration.test.js +0 -205
- package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/hip3-dex.integration.test.js +0 -147
- package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/hyperliquid.integration.test.js +0 -79
- package/dist/__tests__/integration/lighter.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/lighter.integration.test.js +0 -53
- package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +0 -9
- package/dist/__tests__/integration/new-commands-e2e.integration.test.js +0 -236
- package/dist/__tests__/integration/order-verification.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/order-verification.integration.test.js +0 -321
- package/dist/__tests__/integration/pacifica.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/pacifica.integration.test.js +0 -75
- package/dist/__tests__/integration/response-shapes.integration.test.d.ts +0 -1
- package/dist/__tests__/integration/response-shapes.integration.test.js +0 -278
- package/dist/__tests__/liquidity.test.d.ts +0 -1
- package/dist/__tests__/liquidity.test.js +0 -225
- package/dist/__tests__/plan-executor.test.d.ts +0 -1
- package/dist/__tests__/plan-executor.test.js +0 -314
- package/dist/__tests__/position-history.test.d.ts +0 -1
- package/dist/__tests__/position-history.test.js +0 -367
- package/dist/__tests__/retry.test.d.ts +0 -1
- package/dist/__tests__/retry.test.js +0 -310
- package/dist/__tests__/risk-assessment.test.d.ts +0 -1
- package/dist/__tests__/risk-assessment.test.js +0 -288
- package/dist/__tests__/security-adversarial.test.d.ts +0 -1
- package/dist/__tests__/security-adversarial.test.js +0 -574
- package/dist/__tests__/strategies.test.d.ts +0 -1
- package/dist/__tests__/strategies.test.js +0 -539
- package/dist/__tests__/trade-execution.test.d.ts +0 -1
- package/dist/__tests__/trade-execution.test.js +0 -129
- package/dist/__tests__/trade-validator.test.d.ts +0 -1
- package/dist/__tests__/trade-validator.test.js +0 -655
- package/dist/__tests__/utils.test.d.ts +0 -1
- package/dist/__tests__/utils.test.js +0 -76
- package/dist/arb/reconcile.d.ts +0 -28
- package/dist/arb/reconcile.js +0 -162
- package/dist/commands/alert.d.ts +0 -2
- package/dist/commands/alert.js +0 -421
- package/dist/commands/analytics.d.ts +0 -3
- package/dist/commands/analytics.js +0 -311
- package/dist/commands/arb-manage.d.ts +0 -5
- package/dist/commands/arb-manage.js +0 -5
- package/dist/commands/dashboard.d.ts +0 -3
- package/dist/commands/dashboard.js +0 -176
- package/dist/commands/deposit.d.ts +0 -4
- package/dist/commands/deposit.js +0 -573
- package/dist/commands/dex.d.ts +0 -3
- package/dist/commands/dex.js +0 -120
- package/dist/commands/env.d.ts +0 -2
- package/dist/commands/env.js +0 -136
- package/dist/commands/funding.d.ts +0 -3
- package/dist/commands/funding.js +0 -554
- package/dist/commands/gap.d.ts +0 -2
- package/dist/commands/gap.js +0 -305
- package/dist/commands/health.d.ts +0 -2
- package/dist/commands/health.js +0 -67
- package/dist/commands/multileg.d.ts +0 -16
- package/dist/commands/multileg.js +0 -174
- package/dist/commands/pnl.d.ts +0 -12
- package/dist/commands/pnl.js +0 -243
- package/dist/commands/run.d.ts +0 -3
- package/dist/commands/run.js +0 -202
- package/dist/commands/stream.d.ts +0 -5
- package/dist/commands/stream.js +0 -200
- package/dist/commands/withdraw.d.ts +0 -3
- package/dist/commands/withdraw.js +0 -187
- package/dist/exchanges/edgex.d.ts +0 -62
- package/dist/exchanges/edgex.js +0 -552
- package/dist/exchanges/nado.d.ts +0 -52
- package/dist/exchanges/nado.js +0 -223
- package/dist/funding/index.d.ts +0 -3
- package/dist/funding/index.js +0 -3
- package/dist/retry.d.ts +0 -74
- package/dist/retry.js +0 -129
- package/dist/ws/market-feed.d.ts +0 -74
- package/dist/ws/market-feed.js +0 -227
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Hyperliquid } from "hyperliquid";
|
|
2
2
|
import type { ExchangeAdapter, ExchangeMarketInfo, ExchangePosition, ExchangeOrder, ExchangeBalance, ExchangeTrade, ExchangeFundingPayment, ExchangeKline } from "./interface.js";
|
|
3
3
|
import type { EvmSigner } from "../signer/index.js";
|
|
4
|
+
import type { AgentMeta } from "../settings.js";
|
|
4
5
|
export declare class HyperliquidAdapter implements ExchangeAdapter {
|
|
5
6
|
readonly name = "hyperliquid";
|
|
6
7
|
readonly chain = "evm";
|
|
@@ -11,6 +12,9 @@ export declare class HyperliquidAdapter implements ExchangeAdapter {
|
|
|
11
12
|
private _privateKey;
|
|
12
13
|
private _testnet;
|
|
13
14
|
private _evmSigner?;
|
|
15
|
+
private _agentSigner?;
|
|
16
|
+
private _agentMeta?;
|
|
17
|
+
private _useNoAgent;
|
|
14
18
|
private _assetMap;
|
|
15
19
|
private _assetMapReverse;
|
|
16
20
|
private _szDecimalsMap;
|
|
@@ -29,6 +33,16 @@ export declare class HyperliquidAdapter implements ExchangeAdapter {
|
|
|
29
33
|
get isTestnet(): boolean;
|
|
30
34
|
/** Inject an external EVM signer. Call before init() to skip LocalEvmSigner creation. */
|
|
31
35
|
setSigner(signer: EvmSigner): void;
|
|
36
|
+
/** Tier 1: inject agent OWS wallet signer. */
|
|
37
|
+
setAgentSigner(meta: AgentMeta, signer: EvmSigner): void;
|
|
38
|
+
/** Bypass Tier 1 (agent) — use for --no-agent flag. */
|
|
39
|
+
setNoAgent(noAgent: boolean): void;
|
|
40
|
+
/** True only when ALL THREE tiers are unconfigured. */
|
|
41
|
+
get isReadOnly(): boolean;
|
|
42
|
+
/** Which signer tier is currently active (for debug/diagnostics). */
|
|
43
|
+
get activeSignerTier(): "agent" | "master" | "pk" | null;
|
|
44
|
+
/** Resolve the active signer across three tiers. */
|
|
45
|
+
private _resolveSigner;
|
|
32
46
|
init(): Promise<void>;
|
|
33
47
|
/** Load asset index map — supports native and HIP-3 dex. */
|
|
34
48
|
private _loadAssetMap;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hyperliquid } from "hyperliquid";
|
|
2
2
|
import { LocalEvmSigner } from "../signer/index.js";
|
|
3
|
+
import { PerpError } from "../errors.js";
|
|
3
4
|
export class HyperliquidAdapter {
|
|
4
5
|
name = "hyperliquid";
|
|
5
6
|
chain = "evm";
|
|
@@ -9,7 +10,10 @@ export class HyperliquidAdapter {
|
|
|
9
10
|
_address;
|
|
10
11
|
_privateKey;
|
|
11
12
|
_testnet;
|
|
12
|
-
_evmSigner;
|
|
13
|
+
_evmSigner; // Tier 2: OWS master
|
|
14
|
+
_agentSigner; // Tier 1: agent OWS wallet
|
|
15
|
+
_agentMeta; // Tier 1 metadata
|
|
16
|
+
_useNoAgent = false; // --no-agent bypass flag
|
|
13
17
|
_assetMap = new Map();
|
|
14
18
|
_assetMapReverse = new Map();
|
|
15
19
|
_szDecimalsMap = new Map(); // symbol → szDecimals
|
|
@@ -55,6 +59,65 @@ export class HyperliquidAdapter {
|
|
|
55
59
|
/** Inject an external EVM signer. Call before init() to skip LocalEvmSigner creation. */
|
|
56
60
|
setSigner(signer) {
|
|
57
61
|
this._evmSigner = signer;
|
|
62
|
+
this._address = signer.getAddress();
|
|
63
|
+
}
|
|
64
|
+
/** Tier 1: inject agent OWS wallet signer. */
|
|
65
|
+
setAgentSigner(meta, signer) {
|
|
66
|
+
this._agentMeta = meta;
|
|
67
|
+
this._agentSigner = signer;
|
|
68
|
+
}
|
|
69
|
+
/** Bypass Tier 1 (agent) — use for --no-agent flag. */
|
|
70
|
+
setNoAgent(noAgent) {
|
|
71
|
+
this._useNoAgent = noAgent;
|
|
72
|
+
}
|
|
73
|
+
/** True only when ALL THREE tiers are unconfigured. */
|
|
74
|
+
get isReadOnly() {
|
|
75
|
+
return !this._agentSigner && !this._evmSigner && !this._privateKey;
|
|
76
|
+
}
|
|
77
|
+
/** Which signer tier is currently active (for debug/diagnostics). */
|
|
78
|
+
get activeSignerTier() {
|
|
79
|
+
try {
|
|
80
|
+
return this._resolveSigner().tier;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/** Resolve the active signer across three tiers. */
|
|
87
|
+
_resolveSigner() {
|
|
88
|
+
// Tier 1: agent (when registered, not expired, --no-agent NOT set)
|
|
89
|
+
if (!this._useNoAgent && this._agentSigner && this._agentMeta) {
|
|
90
|
+
const expiresAt = new Date(this._agentMeta.expiresAt).getTime();
|
|
91
|
+
const expired = expiresAt < Date.now();
|
|
92
|
+
if (expired) {
|
|
93
|
+
// Skip Tier 1 only if Tier 2/3 available; otherwise throw AGENT_EXPIRED
|
|
94
|
+
if (!this._evmSigner && !this._privateKey) {
|
|
95
|
+
throw new PerpError("AGENT_EXPIRED", "Agent wallet has expired", {
|
|
96
|
+
remediation: "perp wallet agent approve hyperliquid --rotate",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// fall through to Tier 2/3
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return { tier: "agent", signer: this._agentSigner };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Tier 2: OWS master (_evmSigner set via setSigner)
|
|
106
|
+
if (this._evmSigner) {
|
|
107
|
+
return { tier: "master", signer: this._evmSigner };
|
|
108
|
+
}
|
|
109
|
+
// Tier 3: PK direct (_privateKey → _evmSigner created in init())
|
|
110
|
+
// After init(), PK is reflected in _evmSigner. But if init() hasn't run
|
|
111
|
+
// yet, _privateKey is non-empty. Either way this path is reachable only
|
|
112
|
+
// if _evmSigner is set, so Tier 2 catches it. This guard is for the edge
|
|
113
|
+
// case where init() failed but _privateKey is set — throw a clear error.
|
|
114
|
+
if (this._privateKey) {
|
|
115
|
+
throw new PerpError("NO_SIGNER_AVAILABLE", "Hyperliquid adapter has a private key but init() has not been called", { remediation: "Call adapter.init() before using mutating methods" });
|
|
116
|
+
}
|
|
117
|
+
// No signer at any tier
|
|
118
|
+
throw new PerpError("NO_SIGNER_AVAILABLE", "No signing path configured for Hyperliquid", {
|
|
119
|
+
remediation: "Run one of: (a) perp wallet agent approve hyperliquid --master <wallet>; (b) perp wallet generate && perp -e hyperliquid --ows <wallet>; (c) export PRIVATE_KEY=0x...",
|
|
120
|
+
});
|
|
58
121
|
}
|
|
59
122
|
async init() {
|
|
60
123
|
// Suppress "WebSocket connected" noise from hyperliquid SDK
|
|
@@ -71,6 +134,9 @@ export class HyperliquidAdapter {
|
|
|
71
134
|
this._evmSigner = await LocalEvmSigner.create(this._privateKey);
|
|
72
135
|
this._address = this._evmSigner.getAddress();
|
|
73
136
|
}
|
|
137
|
+
else if (this._evmSigner && !this._address) {
|
|
138
|
+
this._address = this._evmSigner.getAddress();
|
|
139
|
+
}
|
|
74
140
|
// Build asset index map
|
|
75
141
|
await this._loadAssetMap();
|
|
76
142
|
}
|
|
@@ -479,9 +545,8 @@ export class HyperliquidAdapter {
|
|
|
479
545
|
* Bypasses the SDK entirely — works for order, batchModify, twapOrder, etc.
|
|
480
546
|
*/
|
|
481
547
|
ensureSigner() {
|
|
482
|
-
if
|
|
483
|
-
|
|
484
|
-
}
|
|
548
|
+
// Resolves across all tiers; throws PerpError if none available
|
|
549
|
+
this._resolveSigner();
|
|
485
550
|
}
|
|
486
551
|
/**
|
|
487
552
|
* Normalize trailing zeros from price ('p') and size ('s') string fields
|
|
@@ -518,7 +583,7 @@ export class HyperliquidAdapter {
|
|
|
518
583
|
return result;
|
|
519
584
|
}
|
|
520
585
|
async _signAndSendAction(action) {
|
|
521
|
-
this.
|
|
586
|
+
const { signer } = this._resolveSigner();
|
|
522
587
|
const { encode } = await import("@msgpack/msgpack");
|
|
523
588
|
const { ethers, keccak256 } = await import("ethers");
|
|
524
589
|
const isMainnet = !this._testnet;
|
|
@@ -557,7 +622,7 @@ export class HyperliquidAdapter {
|
|
|
557
622
|
source: isMainnet ? "a" : "b",
|
|
558
623
|
connectionId: hash,
|
|
559
624
|
};
|
|
560
|
-
const sig = await
|
|
625
|
+
const sig = await signer.signTypedData(phantomDomain, agentTypes, phantomAgent);
|
|
561
626
|
const parsed = ethers.Signature.from(sig);
|
|
562
627
|
const payload = {
|
|
563
628
|
action,
|
|
@@ -635,21 +700,15 @@ export class HyperliquidAdapter {
|
|
|
635
700
|
}
|
|
636
701
|
async cancelOrder(symbol, orderId) {
|
|
637
702
|
this.ensureSigner();
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
// HL SDK expects coin in "ETH-PERP" format (internal symbol convention)
|
|
649
|
-
const resolved = this.resolveSymbol(symbol);
|
|
650
|
-
const result = await this.sdk.exchange.cancelOrder({
|
|
651
|
-
coin: resolved,
|
|
652
|
-
o: parseInt(orderId),
|
|
703
|
+
// HL SDK declares exchange.cancelOrder in types but the runtime ExchangeAPI
|
|
704
|
+
// instance (when constructed with our wallet/agent injection path) does not
|
|
705
|
+
// expose it as a callable method. Use raw cancel action via _signAndSendAction
|
|
706
|
+
// instead — same approach as the HIP-3 dex branch — which works uniformly for
|
|
707
|
+
// standard perps and HIP-3 dexes.
|
|
708
|
+
const assetIndex = await this.getAssetIndex(symbol.toUpperCase());
|
|
709
|
+
const result = await this._signAndSendAction({
|
|
710
|
+
type: "cancel",
|
|
711
|
+
cancels: [{ a: assetIndex, o: parseInt(orderId) }],
|
|
653
712
|
});
|
|
654
713
|
await this._invalidateAccountCache();
|
|
655
714
|
return result;
|
|
@@ -663,21 +722,16 @@ export class HyperliquidAdapter {
|
|
|
663
722
|
return norm(o.symbol) === norm(symbol);
|
|
664
723
|
})
|
|
665
724
|
: orders;
|
|
725
|
+
// Use _signAndSendAction uniformly for both standard perps and HIP-3 dexes.
|
|
726
|
+
// SDK exchange.cancelOrder has the same runtime unavailability as single
|
|
727
|
+
// cancelOrder (see comment there); raw cancel action works for both paths.
|
|
666
728
|
const results = [];
|
|
667
729
|
for (const o of toCancel) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
}));
|
|
674
|
-
}
|
|
675
|
-
else {
|
|
676
|
-
results.push(await this.sdk.exchange.cancelOrder({
|
|
677
|
-
coin: o.symbol,
|
|
678
|
-
o: parseInt(o.orderId),
|
|
679
|
-
}));
|
|
680
|
-
}
|
|
730
|
+
const assetIndex = await this.getAssetIndex(o.symbol);
|
|
731
|
+
results.push(await this._signAndSendAction({
|
|
732
|
+
type: "cancel",
|
|
733
|
+
cancels: [{ a: assetIndex, o: parseInt(o.orderId) }],
|
|
734
|
+
}));
|
|
681
735
|
}
|
|
682
736
|
return results;
|
|
683
737
|
}
|
|
@@ -458,7 +458,7 @@ export class LighterSpotAdapter {
|
|
|
458
458
|
// ── Private helpers ──
|
|
459
459
|
async _placeOrder(opts) {
|
|
460
460
|
if (this._lt.isReadOnly) {
|
|
461
|
-
throw new Error("Spot trading requires a Lighter API key. Run `perp
|
|
461
|
+
throw new Error("Spot trading requires a Lighter API key. Run `perp wallet agent approve lighter` to register an agent.");
|
|
462
462
|
}
|
|
463
463
|
const nonce = await this._getNextNonce();
|
|
464
464
|
const signer = this._lt.signer;
|
|
@@ -501,19 +501,30 @@ export class LighterSpotAdapter {
|
|
|
501
501
|
account_index: String(this._lt.accountIndex),
|
|
502
502
|
api_key_index: String(apiKeyIndex),
|
|
503
503
|
});
|
|
504
|
-
|
|
504
|
+
const nonce = res.nonce ?? res.next_nonce;
|
|
505
|
+
// SSOT rule #2: refuse to silently default to nonce 0. A stale or missing
|
|
506
|
+
// nonce will cause Lighter to reject the tx with cryptic errors and the
|
|
507
|
+
// user has no way to diagnose the upstream failure.
|
|
508
|
+
if (nonce === undefined || !Number.isFinite(nonce) || nonce < 0) {
|
|
509
|
+
throw new Error(`Lighter /nextNonce returned no valid nonce for account_index=${this._lt.accountIndex} ` +
|
|
510
|
+
`api_key_index=${apiKeyIndex}; raw response=${JSON.stringify(res)}`);
|
|
511
|
+
}
|
|
512
|
+
return nonce;
|
|
505
513
|
}
|
|
506
514
|
async _sendTx(signed) {
|
|
507
515
|
if (signed.error)
|
|
508
516
|
throw new Error(`Signer error: ${signed.error}`);
|
|
509
517
|
if (!signed.txInfo)
|
|
510
518
|
throw new Error("Signer returned empty txInfo");
|
|
519
|
+
if (signed.txType === undefined || !Number.isFinite(signed.txType)) {
|
|
520
|
+
throw new Error(`Lighter signer returned no txType (received ${signed.txType}); refusing to send with default 0.`);
|
|
521
|
+
}
|
|
511
522
|
const baseUrl = this._lt._baseUrl;
|
|
512
523
|
const res = await fetch(`${baseUrl}/api/v1/sendTx`, {
|
|
513
524
|
method: "POST",
|
|
514
525
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
515
526
|
body: new URLSearchParams({
|
|
516
|
-
tx_type: String(signed.txType
|
|
527
|
+
tx_type: String(signed.txType),
|
|
517
528
|
tx_info: signed.txInfo,
|
|
518
529
|
}),
|
|
519
530
|
});
|
|
@@ -528,8 +539,11 @@ export class LighterSpotAdapter {
|
|
|
528
539
|
for (const [k, v] of Object.entries(params))
|
|
529
540
|
url.searchParams.set(k, v);
|
|
530
541
|
const res = await fetch(url.toString());
|
|
531
|
-
if (!res.ok)
|
|
532
|
-
|
|
542
|
+
if (!res.ok) {
|
|
543
|
+
const text = await res.text();
|
|
544
|
+
const clean = text.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim().slice(0, 120);
|
|
545
|
+
throw new Error(`GET ${path} failed (${res.status}): ${clean}`);
|
|
546
|
+
}
|
|
533
547
|
return res.json();
|
|
534
548
|
}
|
|
535
549
|
async _restGetAuth(path, params) {
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
import type { ExchangeAdapter, ExchangeMarketInfo, ExchangePosition, ExchangeOrder, ExchangeBalance, ExchangeTrade, ExchangeFundingPayment, ExchangeKline } from "./interface.js";
|
|
2
2
|
import type { EvmSigner } from "../signer/index.js";
|
|
3
|
+
import type { AgentMeta } from "../settings.js";
|
|
3
4
|
import type { WasmSignerClient as WasmSignerClientType } from "lighter-ts-sdk";
|
|
5
|
+
/** Lighter trigger orderType enum (matches WASM signer). */
|
|
6
|
+
export declare const LIGHTER_ORDER_TYPE: {
|
|
7
|
+
readonly STOP_LOSS: 2;
|
|
8
|
+
readonly STOP_LOSS_LIMIT: 3;
|
|
9
|
+
readonly TAKE_PROFIT: 4;
|
|
10
|
+
readonly TAKE_PROFIT_LIMIT: 5;
|
|
11
|
+
};
|
|
12
|
+
export type LighterTriggerOrderType = typeof LIGHTER_ORDER_TYPE.STOP_LOSS | typeof LIGHTER_ORDER_TYPE.STOP_LOSS_LIMIT | typeof LIGHTER_ORDER_TYPE.TAKE_PROFIT | typeof LIGHTER_ORDER_TYPE.TAKE_PROFIT_LIMIT;
|
|
13
|
+
/**
|
|
14
|
+
* Pick Lighter trigger orderType from side, trigger price, and current mark.
|
|
15
|
+
*
|
|
16
|
+
* STOP_LOSS protects against adverse moves; TAKE_PROFIT locks in favorable moves.
|
|
17
|
+
* buy + trigger >= mark → STOP_LOSS (close short on rise / breakout entry)
|
|
18
|
+
* buy + trigger < mark → TAKE_PROFIT (close short on fall)
|
|
19
|
+
* sell + trigger <= mark → STOP_LOSS (close long on fall)
|
|
20
|
+
* sell + trigger > mark → TAKE_PROFIT (close long on rise)
|
|
21
|
+
*
|
|
22
|
+
* Tied trigger==mark is classified as STOP_LOSS — the protective default.
|
|
23
|
+
*/
|
|
24
|
+
export declare function classifyTriggerOrderType(side: "buy" | "sell", triggerPrice: number, markPrice: number, isMarket: boolean): LighterTriggerOrderType;
|
|
4
25
|
export declare class LighterAdapter implements ExchangeAdapter {
|
|
5
26
|
readonly name = "lighter";
|
|
6
27
|
readonly chain = "ethereum";
|
|
@@ -23,6 +44,15 @@ export declare class LighterAdapter implements ExchangeAdapter {
|
|
|
23
44
|
private _marketsCache;
|
|
24
45
|
private _marketsCacheTime;
|
|
25
46
|
private static readonly CACHE_TTL;
|
|
47
|
+
/** Tier 1 metadata (Phase 2d). When set + not expired + !_useNoAgent, agent
|
|
48
|
+
* apiKeyIndex/publicKey/accountIndex take priority over master/PK env. */
|
|
49
|
+
private _agentMeta?;
|
|
50
|
+
/** Tier 1 secp256k1 hex private key (40 bytes) for the agent slot. Lighter
|
|
51
|
+
* has no OWS curve binding for raw L2 keys; this is held in-memory only when
|
|
52
|
+
* passed by the caller (e.g. immediately after `setupApiKey()` or via env). */
|
|
53
|
+
private _agentApiKey?;
|
|
54
|
+
/** --no-agent bypass flag. */
|
|
55
|
+
private _useNoAgent;
|
|
26
56
|
/**
|
|
27
57
|
* @param evmKey EVM private key (0x-prefixed, 32 bytes) — for deposits & key registration
|
|
28
58
|
* @param testnet Use testnet (chain ID 300) instead of mainnet (chain ID 304)
|
|
@@ -31,6 +61,7 @@ export declare class LighterAdapter implements ExchangeAdapter {
|
|
|
31
61
|
constructor(evmKey: string, testnet?: boolean, opts?: {
|
|
32
62
|
apiKey?: string;
|
|
33
63
|
accountIndex?: number;
|
|
64
|
+
apiKeyIndex?: number;
|
|
34
65
|
});
|
|
35
66
|
get signer(): {
|
|
36
67
|
createAuthToken(deadline: number): Promise<{
|
|
@@ -43,6 +74,41 @@ export declare class LighterAdapter implements ExchangeAdapter {
|
|
|
43
74
|
get isReadOnly(): boolean;
|
|
44
75
|
/** Inject an external EVM signer. Call before init() to skip LocalEvmSigner creation. */
|
|
45
76
|
setSigner(signer: EvmSigner): void;
|
|
77
|
+
/**
|
|
78
|
+
* Tier 1: inject a Lighter agent's slot+L2 key (Phase 2d).
|
|
79
|
+
*
|
|
80
|
+
* Caller supplies the AgentMeta (settings.agents.lighter[name]) plus the
|
|
81
|
+
* 40-byte L2 private key (hex). Adapter swaps `_apiKey`/`_apiKeyIndex` to
|
|
82
|
+
* the agent's slot/key so all subsequent `signCreateOrder`/`signCancelOrder`
|
|
83
|
+
* etc. flow through the agent identity.
|
|
84
|
+
*
|
|
85
|
+
* NOTE: when the agent path is active, `_address` (master EVM) is left
|
|
86
|
+
* unchanged because Lighter REST queries (account, balance, positions) use
|
|
87
|
+
* `account_index` not address. The L1 EVM identity is irrelevant for the
|
|
88
|
+
* hot-path; only the L2 secp256k1 key is consulted.
|
|
89
|
+
*/
|
|
90
|
+
setAgentSigner(meta: AgentMeta, agentApiKey: string): void;
|
|
91
|
+
/** Bypass Tier 1 (agent) — used for `--no-agent` flag. */
|
|
92
|
+
setNoAgent(noAgent: boolean): void;
|
|
93
|
+
/** Which signer tier is currently active (for debug/diagnostics). */
|
|
94
|
+
get activeSignerTier(): "agent" | "master" | "pk" | null;
|
|
95
|
+
/**
|
|
96
|
+
* Resolve the active signer tier across three tiers (Phase 2d).
|
|
97
|
+
*
|
|
98
|
+
* Tier 1 (agent): active when AgentMeta + agent L2 key set, not expired,
|
|
99
|
+
* and `--no-agent` NOT set. The actual swap of `_apiKeyIndex`/`_apiKey`
|
|
100
|
+
* happens in `init()` (or via `_activateAgentTier()` post-init).
|
|
101
|
+
* Tier 2 (master): active when an OWS master EVM signer was injected via
|
|
102
|
+
* `setSigner()`. Used for the L1 ChangePubKey path (auto-setup at slot 4).
|
|
103
|
+
* Tier 3 (pk): active when constructor was supplied an evmKey. Same role
|
|
104
|
+
* as Tier 2 — signs L1 ChangePubKey for auto-setup.
|
|
105
|
+
*
|
|
106
|
+
* Lighter is unique among the 4 DEXs in that the actual order-signing is
|
|
107
|
+
* always done by the L2 secp256k1 key (`_apiKey`), regardless of tier. The
|
|
108
|
+
* tiers differ in WHICH L2 key is loaded: Tier 1 uses the agent's slot/key
|
|
109
|
+
* from AgentMeta; Tier 2/3 use whatever the env/auto-setup produced.
|
|
110
|
+
*/
|
|
111
|
+
private _resolveSigner;
|
|
46
112
|
init(): Promise<void>;
|
|
47
113
|
/** Populate marketMap + decimals from /orderBooks API (safe to call multiple times) */
|
|
48
114
|
private _refreshMarketMap;
|