perp-cli 0.11.0 → 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.
Files changed (240) hide show
  1. package/README.md +30 -25
  2. package/dist/_stderr-filter.d.ts +0 -0
  3. package/dist/_stderr-filter.js +30 -0
  4. package/dist/agent-wallet/expiry.d.ts +15 -0
  5. package/dist/agent-wallet/expiry.js +26 -0
  6. package/dist/agent-wallet/lighter-keystore.d.ts +34 -0
  7. package/dist/agent-wallet/lighter-keystore.js +232 -0
  8. package/dist/agent-wallet/passphrase.d.ts +13 -0
  9. package/dist/agent-wallet/passphrase.js +54 -0
  10. package/dist/agent-wallet/signing-strategy.d.ts +67 -0
  11. package/dist/agent-wallet/signing-strategy.js +51 -0
  12. package/dist/agent-wallet/store.d.ts +37 -0
  13. package/dist/agent-wallet/store.js +172 -0
  14. package/dist/api/public/_http.d.ts +15 -0
  15. package/dist/api/public/_http.js +24 -0
  16. package/dist/api/public/hyperliquid.js +36 -24
  17. package/dist/api/public/lighter.d.ts +7 -1
  18. package/dist/api/public/lighter.js +85 -37
  19. package/dist/api/public/pacifica.js +47 -18
  20. package/dist/bridge-engine.js +102 -49
  21. package/dist/cli-spec.js +1 -2
  22. package/dist/commands/account.js +2 -500
  23. package/dist/commands/agent.d.ts +8 -2
  24. package/dist/commands/agent.js +1903 -366
  25. package/dist/commands/alerts.js +3 -1
  26. package/dist/commands/arb/index.js +37 -7
  27. package/dist/commands/arb-auto.js +44 -11
  28. package/dist/commands/arb.js +83 -34
  29. package/dist/commands/bot.d.ts +1 -1
  30. package/dist/commands/bot.js +18 -14
  31. package/dist/commands/bridge.d.ts +1 -1
  32. package/dist/commands/bridge.js +4 -4
  33. package/dist/commands/funds.d.ts +1 -1
  34. package/dist/commands/funds.js +18 -68
  35. package/dist/commands/jobs.d.ts +1 -1
  36. package/dist/commands/jobs.js +2 -2
  37. package/dist/commands/manage.d.ts +1 -1
  38. package/dist/commands/manage.js +12 -84
  39. package/dist/commands/plan.d.ts +1 -1
  40. package/dist/commands/plan.js +2 -2
  41. package/dist/commands/portfolio.d.ts +19 -3
  42. package/dist/commands/portfolio.js +747 -163
  43. package/dist/commands/rebalance.d.ts +1 -1
  44. package/dist/commands/rebalance.js +18 -12
  45. package/dist/commands/risk.js +2 -6
  46. package/dist/commands/settings.js +30 -53
  47. package/dist/commands/trade.js +16 -15
  48. package/dist/commands/wallet.d.ts +2 -1
  49. package/dist/commands/wallet.js +756 -28
  50. package/dist/errors.d.ts +64 -0
  51. package/dist/errors.js +82 -2
  52. package/dist/event-stream.js +12 -1
  53. package/dist/exchanges/aster-typed-data.d.ts +156 -0
  54. package/dist/exchanges/aster-typed-data.js +189 -0
  55. package/dist/exchanges/aster.d.ts +60 -11
  56. package/dist/exchanges/aster.js +278 -136
  57. package/dist/exchanges/hyperliquid.d.ts +14 -0
  58. package/dist/exchanges/hyperliquid.js +84 -34
  59. package/dist/exchanges/lighter-spot.js +14 -3
  60. package/dist/exchanges/lighter.d.ts +66 -0
  61. package/dist/exchanges/lighter.js +199 -27
  62. package/dist/exchanges/pacifica-typed-data.d.ts +75 -0
  63. package/dist/exchanges/pacifica-typed-data.js +74 -0
  64. package/dist/exchanges/pacifica.d.ts +29 -1
  65. package/dist/exchanges/pacifica.js +94 -6
  66. package/dist/funding/rates.js +59 -21
  67. package/dist/guardrail/perp-guardrail.js +9 -1
  68. package/dist/index.d.ts +1 -1
  69. package/dist/index.js +255 -360
  70. package/dist/jobs.js +3 -1
  71. package/dist/mcp-server.js +85 -83
  72. package/dist/position-history.js +47 -4
  73. package/dist/settings.d.ts +67 -3
  74. package/dist/settings.js +1 -2
  75. package/dist/signer/ows-evm.d.ts +14 -0
  76. package/dist/signer/ows-evm.js +28 -9
  77. package/dist/smart-order.d.ts +8 -2
  78. package/dist/smart-order.js +46 -11
  79. package/dist/strategies/funding-arb.js +29 -8
  80. package/dist/trade-validator.js +30 -5
  81. package/dist/utils.d.ts +10 -0
  82. package/dist/utils.js +19 -0
  83. package/package.json +2 -2
  84. package/skills/perp-cli/SKILL.md +23 -14
  85. package/skills/perp-cli/references/agent-operations.md +6 -4
  86. package/skills/perp-cli/references/commands.md +55 -34
  87. package/skills/perp-cli/scripts/preflight.sh +4 -4
  88. package/skills/perp-cli/scripts/spot-perp-scan.sh +2 -2
  89. package/dist/__tests__/alert-logic.test.d.ts +0 -1
  90. package/dist/__tests__/alert-logic.test.js +0 -107
  91. package/dist/__tests__/arb-auto-3dex.test.d.ts +0 -1
  92. package/dist/__tests__/arb-auto-3dex.test.js +0 -397
  93. package/dist/__tests__/arb-history-stats.test.d.ts +0 -1
  94. package/dist/__tests__/arb-history-stats.test.js +0 -176
  95. package/dist/__tests__/arb-logic.test.d.ts +0 -1
  96. package/dist/__tests__/arb-logic.test.js +0 -84
  97. package/dist/__tests__/arb-manage.test.d.ts +0 -1
  98. package/dist/__tests__/arb-manage.test.js +0 -253
  99. package/dist/__tests__/arb-new-features.test.d.ts +0 -1
  100. package/dist/__tests__/arb-new-features.test.js +0 -457
  101. package/dist/__tests__/arb-sizing.test.d.ts +0 -1
  102. package/dist/__tests__/arb-sizing.test.js +0 -48
  103. package/dist/__tests__/arb-state.test.d.ts +0 -1
  104. package/dist/__tests__/arb-state.test.js +0 -284
  105. package/dist/__tests__/arb-userflow.test.d.ts +0 -1
  106. package/dist/__tests__/arb-userflow.test.js +0 -945
  107. package/dist/__tests__/arb-utils.test.d.ts +0 -1
  108. package/dist/__tests__/arb-utils.test.js +0 -264
  109. package/dist/__tests__/bot-conditions.test.d.ts +0 -1
  110. package/dist/__tests__/bot-conditions.test.js +0 -341
  111. package/dist/__tests__/client-id-tracker.test.d.ts +0 -1
  112. package/dist/__tests__/client-id-tracker.test.js +0 -137
  113. package/dist/__tests__/commands/new-atomic-commands.test.d.ts +0 -1
  114. package/dist/__tests__/commands/new-atomic-commands.test.js +0 -502
  115. package/dist/__tests__/commands/order-intent.test.d.ts +0 -1
  116. package/dist/__tests__/commands/order-intent.test.js +0 -600
  117. package/dist/__tests__/commands/trade-commands.test.d.ts +0 -1
  118. package/dist/__tests__/commands/trade-commands.test.js +0 -821
  119. package/dist/__tests__/config.test.d.ts +0 -1
  120. package/dist/__tests__/config.test.js +0 -86
  121. package/dist/__tests__/cross-chain-margin.test.d.ts +0 -1
  122. package/dist/__tests__/cross-chain-margin.test.js +0 -287
  123. package/dist/__tests__/dex-asset-map.test.d.ts +0 -1
  124. package/dist/__tests__/dex-asset-map.test.js +0 -191
  125. package/dist/__tests__/errors.test.d.ts +0 -1
  126. package/dist/__tests__/errors.test.js +0 -110
  127. package/dist/__tests__/event-stream.test.d.ts +0 -1
  128. package/dist/__tests__/event-stream.test.js +0 -276
  129. package/dist/__tests__/exchanges/interface.test.d.ts +0 -1
  130. package/dist/__tests__/exchanges/interface.test.js +0 -132
  131. package/dist/__tests__/exchanges/mock-adapter.d.ts +0 -69
  132. package/dist/__tests__/exchanges/mock-adapter.js +0 -137
  133. package/dist/__tests__/execution-log.test.d.ts +0 -1
  134. package/dist/__tests__/execution-log.test.js +0 -106
  135. package/dist/__tests__/funding-calc.test.d.ts +0 -1
  136. package/dist/__tests__/funding-calc.test.js +0 -71
  137. package/dist/__tests__/funding-history.test.d.ts +0 -1
  138. package/dist/__tests__/funding-history.test.js +0 -343
  139. package/dist/__tests__/funding-rates.test.d.ts +0 -1
  140. package/dist/__tests__/funding-rates.test.js +0 -342
  141. package/dist/__tests__/funding.test.d.ts +0 -1
  142. package/dist/__tests__/funding.test.js +0 -173
  143. package/dist/__tests__/gap-logic.test.d.ts +0 -1
  144. package/dist/__tests__/gap-logic.test.js +0 -43
  145. package/dist/__tests__/hip3-dex.test.d.ts +0 -1
  146. package/dist/__tests__/hip3-dex.test.js +0 -234
  147. package/dist/__tests__/integration/agent-features.integration.test.d.ts +0 -1
  148. package/dist/__tests__/integration/agent-features.integration.test.js +0 -553
  149. package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +0 -13
  150. package/dist/__tests__/integration/atomic-commands.integration.test.js +0 -246
  151. package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +0 -1
  152. package/dist/__tests__/integration/bridge-simulation.integration.test.js +0 -453
  153. package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +0 -1
  154. package/dist/__tests__/integration/bridge-strict.integration.test.js +0 -812
  155. package/dist/__tests__/integration/bridge.integration.test.d.ts +0 -1
  156. package/dist/__tests__/integration/bridge.integration.test.js +0 -309
  157. package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +0 -1
  158. package/dist/__tests__/integration/cli-e2e.integration.test.js +0 -202
  159. package/dist/__tests__/integration/dex-arb.integration.test.d.ts +0 -1
  160. package/dist/__tests__/integration/dex-arb.integration.test.js +0 -116
  161. package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +0 -13
  162. package/dist/__tests__/integration/envelope-consistency.integration.test.js +0 -205
  163. package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +0 -1
  164. package/dist/__tests__/integration/hip3-dex.integration.test.js +0 -147
  165. package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +0 -1
  166. package/dist/__tests__/integration/hyperliquid.integration.test.js +0 -79
  167. package/dist/__tests__/integration/lighter.integration.test.d.ts +0 -1
  168. package/dist/__tests__/integration/lighter.integration.test.js +0 -53
  169. package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +0 -9
  170. package/dist/__tests__/integration/new-commands-e2e.integration.test.js +0 -236
  171. package/dist/__tests__/integration/order-verification.integration.test.d.ts +0 -1
  172. package/dist/__tests__/integration/order-verification.integration.test.js +0 -321
  173. package/dist/__tests__/integration/pacifica.integration.test.d.ts +0 -1
  174. package/dist/__tests__/integration/pacifica.integration.test.js +0 -75
  175. package/dist/__tests__/integration/response-shapes.integration.test.d.ts +0 -1
  176. package/dist/__tests__/integration/response-shapes.integration.test.js +0 -278
  177. package/dist/__tests__/liquidity.test.d.ts +0 -1
  178. package/dist/__tests__/liquidity.test.js +0 -225
  179. package/dist/__tests__/plan-executor.test.d.ts +0 -1
  180. package/dist/__tests__/plan-executor.test.js +0 -314
  181. package/dist/__tests__/position-history.test.d.ts +0 -1
  182. package/dist/__tests__/position-history.test.js +0 -367
  183. package/dist/__tests__/retry.test.d.ts +0 -1
  184. package/dist/__tests__/retry.test.js +0 -310
  185. package/dist/__tests__/risk-assessment.test.d.ts +0 -1
  186. package/dist/__tests__/risk-assessment.test.js +0 -288
  187. package/dist/__tests__/security-adversarial.test.d.ts +0 -1
  188. package/dist/__tests__/security-adversarial.test.js +0 -574
  189. package/dist/__tests__/strategies.test.d.ts +0 -1
  190. package/dist/__tests__/strategies.test.js +0 -539
  191. package/dist/__tests__/trade-execution.test.d.ts +0 -1
  192. package/dist/__tests__/trade-execution.test.js +0 -129
  193. package/dist/__tests__/trade-validator.test.d.ts +0 -1
  194. package/dist/__tests__/trade-validator.test.js +0 -655
  195. package/dist/__tests__/utils.test.d.ts +0 -1
  196. package/dist/__tests__/utils.test.js +0 -76
  197. package/dist/arb/reconcile.d.ts +0 -28
  198. package/dist/arb/reconcile.js +0 -162
  199. package/dist/commands/alert.d.ts +0 -2
  200. package/dist/commands/alert.js +0 -421
  201. package/dist/commands/analytics.d.ts +0 -3
  202. package/dist/commands/analytics.js +0 -311
  203. package/dist/commands/arb-manage.d.ts +0 -5
  204. package/dist/commands/arb-manage.js +0 -5
  205. package/dist/commands/dashboard.d.ts +0 -3
  206. package/dist/commands/dashboard.js +0 -176
  207. package/dist/commands/deposit.d.ts +0 -4
  208. package/dist/commands/deposit.js +0 -573
  209. package/dist/commands/dex.d.ts +0 -3
  210. package/dist/commands/dex.js +0 -120
  211. package/dist/commands/env.d.ts +0 -2
  212. package/dist/commands/env.js +0 -136
  213. package/dist/commands/funding.d.ts +0 -3
  214. package/dist/commands/funding.js +0 -554
  215. package/dist/commands/gap.d.ts +0 -2
  216. package/dist/commands/gap.js +0 -305
  217. package/dist/commands/health.d.ts +0 -2
  218. package/dist/commands/health.js +0 -67
  219. package/dist/commands/multileg.d.ts +0 -16
  220. package/dist/commands/multileg.js +0 -174
  221. package/dist/commands/ows.d.ts +0 -2
  222. package/dist/commands/ows.js +0 -887
  223. package/dist/commands/pnl.d.ts +0 -12
  224. package/dist/commands/pnl.js +0 -243
  225. package/dist/commands/run.d.ts +0 -3
  226. package/dist/commands/run.js +0 -202
  227. package/dist/commands/stream.d.ts +0 -5
  228. package/dist/commands/stream.js +0 -200
  229. package/dist/commands/withdraw.d.ts +0 -3
  230. package/dist/commands/withdraw.js +0 -187
  231. package/dist/exchanges/edgex.d.ts +0 -62
  232. package/dist/exchanges/edgex.js +0 -552
  233. package/dist/exchanges/nado.d.ts +0 -52
  234. package/dist/exchanges/nado.js +0 -223
  235. package/dist/funding/index.d.ts +0 -3
  236. package/dist/funding/index.js +0 -3
  237. package/dist/retry.d.ts +0 -74
  238. package/dist/retry.js +0 -129
  239. package/dist/ws/market-feed.d.ts +0 -74
  240. 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
@@ -57,6 +61,64 @@ export class HyperliquidAdapter {
57
61
  this._evmSigner = signer;
58
62
  this._address = signer.getAddress();
59
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
+ });
121
+ }
60
122
  async init() {
61
123
  // Suppress "WebSocket connected" noise from hyperliquid SDK
62
124
  const origLog = console.log;
@@ -483,9 +545,8 @@ export class HyperliquidAdapter {
483
545
  * Bypasses the SDK entirely — works for order, batchModify, twapOrder, etc.
484
546
  */
485
547
  ensureSigner() {
486
- if (!this._evmSigner) {
487
- throw new Error("No private key configured. Run: perp setup");
488
- }
548
+ // Resolves across all tiers; throws PerpError if none available
549
+ this._resolveSigner();
489
550
  }
490
551
  /**
491
552
  * Normalize trailing zeros from price ('p') and size ('s') string fields
@@ -522,7 +583,7 @@ export class HyperliquidAdapter {
522
583
  return result;
523
584
  }
524
585
  async _signAndSendAction(action) {
525
- this.ensureSigner();
586
+ const { signer } = this._resolveSigner();
526
587
  const { encode } = await import("@msgpack/msgpack");
527
588
  const { ethers, keccak256 } = await import("ethers");
528
589
  const isMainnet = !this._testnet;
@@ -561,7 +622,7 @@ export class HyperliquidAdapter {
561
622
  source: isMainnet ? "a" : "b",
562
623
  connectionId: hash,
563
624
  };
564
- const sig = await this._evmSigner.signTypedData(phantomDomain, agentTypes, phantomAgent);
625
+ const sig = await signer.signTypedData(phantomDomain, agentTypes, phantomAgent);
565
626
  const parsed = ethers.Signature.from(sig);
566
627
  const payload = {
567
628
  action,
@@ -639,21 +700,15 @@ export class HyperliquidAdapter {
639
700
  }
640
701
  async cancelOrder(symbol, orderId) {
641
702
  this.ensureSigner();
642
- if (this._dex) {
643
- // HIP-3 dex: use raw cancel action with global asset index
644
- const assetIndex = await this.getAssetIndex(symbol.toUpperCase());
645
- const result = await this._signAndSendAction({
646
- type: "cancel",
647
- cancels: [{ a: assetIndex, o: parseInt(orderId) }],
648
- });
649
- await this._invalidateAccountCache();
650
- return result;
651
- }
652
- // HL SDK expects coin in "ETH-PERP" format (internal symbol convention)
653
- const resolved = this.resolveSymbol(symbol);
654
- const result = await this.sdk.exchange.cancelOrder({
655
- coin: resolved,
656
- 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) }],
657
712
  });
658
713
  await this._invalidateAccountCache();
659
714
  return result;
@@ -667,21 +722,16 @@ export class HyperliquidAdapter {
667
722
  return norm(o.symbol) === norm(symbol);
668
723
  })
669
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.
670
728
  const results = [];
671
729
  for (const o of toCancel) {
672
- if (this._dex) {
673
- const assetIndex = await this.getAssetIndex(o.symbol);
674
- results.push(await this._signAndSendAction({
675
- type: "cancel",
676
- cancels: [{ a: assetIndex, o: parseInt(o.orderId) }],
677
- }));
678
- }
679
- else {
680
- results.push(await this.sdk.exchange.cancelOrder({
681
- coin: o.symbol,
682
- o: parseInt(o.orderId),
683
- }));
684
- }
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
+ }));
685
735
  }
686
736
  return results;
687
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 -e lighter manage setup-api-key` first.");
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
- return res.nonce ?? res.next_nonce ?? 0;
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 ?? 0),
527
+ tx_type: String(signed.txType),
517
528
  tx_info: signed.txInfo,
518
529
  }),
519
530
  });
@@ -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;