perp-cli 0.12.17 → 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.
Files changed (361) hide show
  1. package/README.md +247 -27
  2. package/dist/_stderr-filter.js +0 -1
  3. package/dist/agent-wallet/lighter-keystore.d.ts +11 -0
  4. package/dist/agent-wallet/lighter-keystore.js +92 -10
  5. package/dist/agent-wallet/passphrase.d.ts +1 -1
  6. package/dist/agent-wallet/passphrase.js +16 -3
  7. package/dist/agent-wallet/store.d.ts +37 -4
  8. package/dist/agent-wallet/store.js +132 -0
  9. package/dist/agent-wallet/venue-keystore.d.ts +14 -0
  10. package/dist/agent-wallet/venue-keystore.js +121 -0
  11. package/dist/api/public/hyperliquid.js +4 -4
  12. package/dist/api/public/lighter.js +5 -5
  13. package/dist/api/public/pacifica.d.ts +0 -1
  14. package/dist/api/public/pacifica.js +4 -13
  15. package/dist/api/public/urls.d.ts +9 -1
  16. package/dist/api/public/urls.js +35 -13
  17. package/dist/arb/file-lock.d.ts +11 -0
  18. package/dist/arb/file-lock.js +74 -0
  19. package/dist/arb/index.d.ts +2 -2
  20. package/dist/arb/index.js +2 -2
  21. package/dist/arb/pending-entry.d.ts +32 -0
  22. package/dist/arb/pending-entry.js +77 -0
  23. package/dist/arb/sizing.d.ts +6 -13
  24. package/dist/arb/sizing.js +12 -51
  25. package/dist/arb/state.d.ts +45 -12
  26. package/dist/arb/state.js +214 -42
  27. package/dist/arb/utils.d.ts +20 -6
  28. package/dist/arb/utils.js +43 -39
  29. package/dist/bot/conditions.d.ts +4 -4
  30. package/dist/bot/conditions.js +11 -6
  31. package/dist/bot/config.d.ts +17 -1
  32. package/dist/bot/config.js +48 -12
  33. package/dist/bot/engine.d.ts +61 -2
  34. package/dist/bot/engine.js +610 -144
  35. package/dist/bot/fill-size.d.ts +7 -0
  36. package/dist/bot/fill-size.js +18 -0
  37. package/dist/bot/index.d.ts +1 -1
  38. package/dist/bot/index.js +1 -1
  39. package/dist/bot/perp-order.d.ts +99 -0
  40. package/dist/bot/perp-order.js +299 -0
  41. package/dist/bot/presets.d.ts +7 -0
  42. package/dist/bot/presets.js +17 -0
  43. package/dist/bot/quarantine.d.ts +75 -0
  44. package/dist/bot/quarantine.js +190 -0
  45. package/dist/bot/quote-rules.d.ts +23 -0
  46. package/dist/bot/quote-rules.js +69 -0
  47. package/dist/bot/strategies/avellaneda-mm.d.ts +32 -2
  48. package/dist/bot/strategies/avellaneda-mm.js +72 -70
  49. package/dist/bot/strategies/basis-arb.js +2 -0
  50. package/dist/bot/strategies/dca-strategy.d.ts +2 -0
  51. package/dist/bot/strategies/dca-strategy.js +5 -0
  52. package/dist/bot/strategies/funding-arb-strategy.d.ts +43 -3
  53. package/dist/bot/strategies/funding-arb-strategy.js +571 -380
  54. package/dist/bot/strategies/funding-arb-utils.d.ts +48 -23
  55. package/dist/bot/strategies/funding-arb-utils.js +159 -195
  56. package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
  57. package/dist/bot/strategies/funding-arb-v2.js +258 -47
  58. package/dist/bot/strategies/funding-auto.js +215 -194
  59. package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +14 -0
  60. package/dist/bot/strategies/spot-perp-arb-strategy.js +217 -50
  61. package/dist/bot/strategies/spot-perp-executor.d.ts +73 -19
  62. package/dist/bot/strategies/spot-perp-executor.js +319 -120
  63. package/dist/bot/strategies/spot-perp-state.d.ts +43 -0
  64. package/dist/bot/strategies/spot-perp-state.js +70 -0
  65. package/dist/bot/strategy-types.d.ts +6 -0
  66. package/dist/bot/tui/BotDashboard.d.ts +7 -2
  67. package/dist/bot/tui/BotDashboard.js +6 -3
  68. package/dist/bridge-engine.d.ts +120 -19
  69. package/dist/bridge-engine.js +710 -422
  70. package/dist/bridge-signers.d.ts +54 -0
  71. package/dist/bridge-signers.js +161 -0
  72. package/dist/cache.d.ts +4 -1
  73. package/dist/cache.js +11 -0
  74. package/dist/cli-flags.d.ts +50 -0
  75. package/dist/cli-flags.js +104 -0
  76. package/dist/cli-program.d.ts +35 -0
  77. package/dist/cli-program.js +286 -0
  78. package/dist/cli-spec.js +1 -1
  79. package/dist/client-id-tracker.js +11 -2
  80. package/dist/commands/account.d.ts +7 -1
  81. package/dist/commands/account.js +70 -92
  82. package/dist/commands/agent-import.d.ts +114 -0
  83. package/dist/commands/agent-import.js +794 -0
  84. package/dist/commands/agent.d.ts +232 -2
  85. package/dist/commands/agent.js +1858 -955
  86. package/dist/commands/alerts.d.ts +1 -0
  87. package/dist/commands/alerts.js +31 -28
  88. package/dist/commands/arb/index.d.ts +60 -1
  89. package/dist/commands/arb/index.js +381 -384
  90. package/dist/commands/arb-auto.d.ts +132 -4
  91. package/dist/commands/arb-auto.js +1474 -967
  92. package/dist/commands/arb.d.ts +3 -1
  93. package/dist/commands/arb.js +62 -30
  94. package/dist/commands/bot.js +127 -47
  95. package/dist/commands/bridge.d.ts +2 -1
  96. package/dist/commands/bridge.js +339 -192
  97. package/dist/commands/builder.d.ts +55 -0
  98. package/dist/commands/builder.js +387 -0
  99. package/dist/commands/funds-onchain.d.ts +32 -0
  100. package/dist/commands/funds-onchain.js +286 -0
  101. package/dist/commands/funds.d.ts +25 -0
  102. package/dist/commands/funds.js +939 -245
  103. package/dist/commands/history.js +72 -45
  104. package/dist/commands/init.d.ts +107 -15
  105. package/dist/commands/init.js +1116 -249
  106. package/dist/commands/jobs.d.ts +2 -1
  107. package/dist/commands/jobs.js +77 -15
  108. package/dist/commands/manage.js +136 -91
  109. package/dist/commands/market.d.ts +4 -1
  110. package/dist/commands/market.js +35 -32
  111. package/dist/commands/outcome.d.ts +10 -0
  112. package/dist/commands/outcome.js +396 -0
  113. package/dist/commands/plan.js +52 -12
  114. package/dist/commands/portfolio.d.ts +1 -0
  115. package/dist/commands/portfolio.js +60 -72
  116. package/dist/commands/rebalance.d.ts +2 -1
  117. package/dist/commands/rebalance.js +33 -43
  118. package/dist/commands/risk.js +46 -19
  119. package/dist/commands/settings.d.ts +1 -0
  120. package/dist/commands/settings.js +246 -51
  121. package/dist/commands/trade.d.ts +50 -0
  122. package/dist/commands/trade.js +1943 -695
  123. package/dist/commands/wallet.d.ts +30 -1
  124. package/dist/commands/wallet.js +316 -337
  125. package/dist/config.d.ts +60 -9
  126. package/dist/config.js +252 -82
  127. package/dist/constants.d.ts +29 -4
  128. package/dist/constants.js +62 -20
  129. package/dist/dashboard/server.d.ts +2 -0
  130. package/dist/dashboard/server.js +9 -6
  131. package/dist/dashboard/ui.js +6 -4
  132. package/dist/dashboard/ws-feeds.js +20 -8
  133. package/dist/dex-asset-map.js +5 -3
  134. package/dist/errors.d.ts +122 -0
  135. package/dist/errors.js +194 -0
  136. package/dist/event-stream.js +75 -19
  137. package/dist/evm-master-tx.d.ts +59 -0
  138. package/dist/evm-master-tx.js +154 -0
  139. package/dist/exchanges/adapter-network.d.ts +10 -0
  140. package/dist/exchanges/adapter-network.js +10 -0
  141. package/dist/exchanges/arcus/adapter.d.ts +196 -0
  142. package/dist/exchanges/arcus/adapter.js +1092 -0
  143. package/dist/exchanges/arcus/agent.d.ts +55 -0
  144. package/dist/exchanges/arcus/agent.js +188 -0
  145. package/dist/exchanges/arcus/client.d.ts +63 -0
  146. package/dist/exchanges/arcus/client.js +285 -0
  147. package/dist/exchanges/arcus/public.d.ts +65 -0
  148. package/dist/exchanges/arcus/public.js +138 -0
  149. package/dist/exchanges/arcus/signing.d.ts +176 -0
  150. package/dist/exchanges/arcus/signing.js +288 -0
  151. package/dist/exchanges/arcus/spot-chain.d.ts +60 -0
  152. package/dist/exchanges/arcus/spot-chain.js +241 -0
  153. package/dist/exchanges/arcus/spot-router.d.ts +177 -0
  154. package/dist/exchanges/arcus/spot-router.js +183 -0
  155. package/dist/exchanges/arcus/spot.d.ts +109 -0
  156. package/dist/exchanges/arcus/spot.js +645 -0
  157. package/dist/exchanges/aster-funds.d.ts +127 -0
  158. package/dist/exchanges/aster-funds.js +252 -0
  159. package/dist/exchanges/aster-nonce.d.ts +2 -0
  160. package/dist/exchanges/aster-nonce.js +107 -0
  161. package/dist/exchanges/aster-spot.d.ts +117 -0
  162. package/dist/exchanges/aster-spot.js +686 -0
  163. package/dist/exchanges/aster-typed-data.d.ts +133 -7
  164. package/dist/exchanges/aster-typed-data.js +121 -7
  165. package/dist/exchanges/aster.d.ts +219 -6
  166. package/dist/exchanges/aster.js +1023 -153
  167. package/dist/exchanges/builder-fee.d.ts +254 -0
  168. package/dist/exchanges/builder-fee.js +421 -0
  169. package/dist/exchanges/canonical-symbols.d.ts +32 -0
  170. package/dist/exchanges/canonical-symbols.js +96 -0
  171. package/dist/exchanges/capabilities.d.ts +7 -1
  172. package/dist/exchanges/capabilities.js +7 -0
  173. package/dist/exchanges/hl-signing.d.ts +165 -0
  174. package/dist/exchanges/hl-signing.js +158 -0
  175. package/dist/exchanges/hyperliquid-outcome.d.ts +211 -0
  176. package/dist/exchanges/hyperliquid-outcome.js +646 -0
  177. package/dist/exchanges/hyperliquid-spot.d.ts +44 -5
  178. package/dist/exchanges/hyperliquid-spot.js +308 -117
  179. package/dist/exchanges/hyperliquid.d.ts +462 -57
  180. package/dist/exchanges/hyperliquid.js +1575 -449
  181. package/dist/exchanges/index.d.ts +6 -0
  182. package/dist/exchanges/index.js +4 -0
  183. package/dist/exchanges/interface.d.ts +63 -10
  184. package/dist/exchanges/interface.js +18 -3
  185. package/dist/exchanges/ioc-outcome.d.ts +44 -0
  186. package/dist/exchanges/ioc-outcome.js +57 -0
  187. package/dist/exchanges/legacy-trading-v2.d.ts +39 -0
  188. package/dist/exchanges/legacy-trading-v2.js +940 -0
  189. package/dist/exchanges/lighter-public.d.ts +43 -0
  190. package/dist/exchanges/lighter-public.js +276 -0
  191. package/dist/exchanges/lighter-rh-deposit.d.ts +62 -0
  192. package/dist/exchanges/lighter-rh-deposit.js +185 -0
  193. package/dist/exchanges/lighter-signer.d.ts +123 -0
  194. package/dist/exchanges/lighter-signer.js +215 -0
  195. package/dist/exchanges/lighter-spot.d.ts +91 -13
  196. package/dist/exchanges/lighter-spot.js +595 -233
  197. package/dist/exchanges/lighter.d.ts +611 -39
  198. package/dist/exchanges/lighter.js +1890 -649
  199. package/dist/exchanges/ondo/adapter.d.ts +166 -0
  200. package/dist/exchanges/ondo/adapter.js +835 -0
  201. package/dist/exchanges/ondo/agent.d.ts +31 -0
  202. package/dist/exchanges/ondo/agent.js +101 -0
  203. package/dist/exchanges/ondo/client.d.ts +60 -0
  204. package/dist/exchanges/ondo/client.js +297 -0
  205. package/dist/exchanges/ondo/funds.d.ts +122 -0
  206. package/dist/exchanges/ondo/funds.js +290 -0
  207. package/dist/exchanges/ondo/public.d.ts +95 -0
  208. package/dist/exchanges/ondo/public.js +214 -0
  209. package/dist/exchanges/ondo/spot.d.ts +127 -0
  210. package/dist/exchanges/ondo/spot.js +392 -0
  211. package/dist/exchanges/outcome-interface.d.ts +140 -0
  212. package/dist/exchanges/outcome-interface.js +16 -0
  213. package/dist/exchanges/pacifica-spot.d.ts +83 -0
  214. package/dist/exchanges/pacifica-spot.js +324 -0
  215. package/dist/exchanges/pacifica-typed-data.d.ts +30 -13
  216. package/dist/exchanges/pacifica-typed-data.js +57 -9
  217. package/dist/exchanges/pacifica.d.ts +288 -4
  218. package/dist/exchanges/pacifica.js +1013 -76
  219. package/dist/exchanges/precision.d.ts +47 -0
  220. package/dist/exchanges/precision.js +209 -0
  221. package/dist/exchanges/qfex/adapter.d.ts +341 -0
  222. package/dist/exchanges/qfex/adapter.js +1383 -0
  223. package/dist/exchanges/qfex/agent.d.ts +58 -0
  224. package/dist/exchanges/qfex/agent.js +188 -0
  225. package/dist/exchanges/qfex/client.d.ts +54 -0
  226. package/dist/exchanges/qfex/client.js +138 -0
  227. package/dist/exchanges/qfex/errors.d.ts +70 -0
  228. package/dist/exchanges/qfex/errors.js +236 -0
  229. package/dist/exchanges/qfex/public.d.ts +122 -0
  230. package/dist/exchanges/qfex/public.js +268 -0
  231. package/dist/exchanges/qfex/signing.d.ts +160 -0
  232. package/dist/exchanges/qfex/signing.js +283 -0
  233. package/dist/exchanges/qfex/ws-transport.d.ts +294 -0
  234. package/dist/exchanges/qfex/ws-transport.js +798 -0
  235. package/dist/exchanges/rate-limiter.d.ts +12 -0
  236. package/dist/exchanges/rate-limiter.js +64 -0
  237. package/dist/exchanges/registry.d.ts +14 -2
  238. package/dist/exchanges/registry.js +20 -32
  239. package/dist/exchanges/slippage.d.ts +37 -0
  240. package/dist/exchanges/slippage.js +70 -0
  241. package/dist/exchanges/spot-factory.d.ts +166 -0
  242. package/dist/exchanges/spot-factory.js +431 -0
  243. package/dist/exchanges/spot-interface.d.ts +98 -4
  244. package/dist/exchanges/spot-interface.js +27 -1
  245. package/dist/exchanges/spot-result.d.ts +79 -0
  246. package/dist/exchanges/spot-result.js +273 -0
  247. package/dist/exchanges/trading-v2.d.ts +117 -0
  248. package/dist/exchanges/trading-v2.js +301 -0
  249. package/dist/exchanges/venue-types.d.ts +231 -0
  250. package/dist/exchanges/venue-types.js +87 -0
  251. package/dist/exchanges/venue.d.ts +211 -0
  252. package/dist/exchanges/venue.js +132 -0
  253. package/dist/exchanges/venues/arcus.d.ts +8 -0
  254. package/dist/exchanges/venues/arcus.js +109 -0
  255. package/dist/exchanges/venues/aster.d.ts +49 -0
  256. package/dist/exchanges/venues/aster.js +176 -0
  257. package/dist/exchanges/venues/create-helpers.d.ts +18 -0
  258. package/dist/exchanges/venues/create-helpers.js +199 -0
  259. package/dist/exchanges/venues/hyperliquid.d.ts +23 -0
  260. package/dist/exchanges/venues/hyperliquid.js +126 -0
  261. package/dist/exchanges/venues/index.d.ts +16 -0
  262. package/dist/exchanges/venues/index.js +26 -0
  263. package/dist/exchanges/venues/lighter-family.d.ts +92 -0
  264. package/dist/exchanges/venues/lighter-family.js +166 -0
  265. package/dist/exchanges/venues/lighter-rh.d.ts +7 -0
  266. package/dist/exchanges/venues/lighter-rh.js +58 -0
  267. package/dist/exchanges/venues/lighter.d.ts +6 -0
  268. package/dist/exchanges/venues/lighter.js +55 -0
  269. package/dist/exchanges/venues/ondo.d.ts +34 -0
  270. package/dist/exchanges/venues/ondo.js +140 -0
  271. package/dist/exchanges/venues/pacifica.d.ts +8 -0
  272. package/dist/exchanges/venues/pacifica.js +95 -0
  273. package/dist/exchanges/venues/qfex.d.ts +28 -0
  274. package/dist/exchanges/venues/qfex.js +132 -0
  275. package/dist/execution-log.js +10 -2
  276. package/dist/funding/legacy-public.d.ts +31 -0
  277. package/dist/funding/legacy-public.js +369 -0
  278. package/dist/funding/normalize.d.ts +28 -16
  279. package/dist/funding/normalize.js +61 -29
  280. package/dist/funding/rates.d.ts +46 -11
  281. package/dist/funding/rates.js +228 -277
  282. package/dist/guardrail/perp-guardrail.js +0 -0
  283. package/dist/index.js +156 -684
  284. package/dist/jobs.d.ts +84 -6
  285. package/dist/jobs.js +239 -41
  286. package/dist/landing.d.ts +7 -2
  287. package/dist/landing.js +19 -3
  288. package/dist/mcp-server.d.ts +14 -4
  289. package/dist/mcp-server.js +820 -402
  290. package/dist/mcp-trade-guard.d.ts +16 -0
  291. package/dist/mcp-trade-guard.js +59 -0
  292. package/dist/pacifica/client.d.ts +41 -10
  293. package/dist/pacifica/client.js +167 -63
  294. package/dist/pacifica/constants.d.ts +0 -1
  295. package/dist/pacifica/constants.js +0 -5
  296. package/dist/pacifica/deposit.d.ts +30 -3
  297. package/dist/pacifica/deposit.js +119 -2
  298. package/dist/pacifica/faucet.d.ts +29 -0
  299. package/dist/pacifica/faucet.js +100 -0
  300. package/dist/pacifica/sign-tx.d.ts +15 -0
  301. package/dist/pacifica/sign-tx.js +41 -0
  302. package/dist/pacifica/types/account.d.ts +28 -4
  303. package/dist/pacifica/types/lake.d.ts +20 -5
  304. package/dist/pacifica/types/lake.js +4 -0
  305. package/dist/pacifica/types/market.d.ts +5 -0
  306. package/dist/pacifica/types/order.d.ts +4 -1
  307. package/dist/pacifica/types/position.d.ts +9 -1
  308. package/dist/plan-executor.d.ts +10 -5
  309. package/dist/plan-executor.js +165 -54
  310. package/dist/position-history.js +13 -0
  311. package/dist/rebalance.d.ts +26 -2
  312. package/dist/rebalance.js +86 -7
  313. package/dist/relay-swap.d.ts +232 -0
  314. package/dist/relay-swap.js +630 -0
  315. package/dist/risk.js +5 -1
  316. package/dist/secret-env.d.ts +15 -0
  317. package/dist/secret-env.js +36 -0
  318. package/dist/settings.d.ts +169 -6
  319. package/dist/settings.js +219 -38
  320. package/dist/signer/ows-evm.d.ts +13 -0
  321. package/dist/signer/ows-evm.js +22 -0
  322. package/dist/smart-order.d.ts +9 -0
  323. package/dist/smart-order.js +53 -8
  324. package/dist/strategies/dca.d.ts +6 -2
  325. package/dist/strategies/dca.js +66 -26
  326. package/dist/strategies/funding-arb.d.ts +11 -1
  327. package/dist/strategies/funding-arb.js +202 -89
  328. package/dist/strategies/grid.d.ts +31 -0
  329. package/dist/strategies/grid.js +89 -11
  330. package/dist/strategies/pending-stop.d.ts +8 -0
  331. package/dist/strategies/pending-stop.js +12 -0
  332. package/dist/strategies/split-order.d.ts +45 -17
  333. package/dist/strategies/split-order.js +157 -91
  334. package/dist/strategies/trailing-stop.d.ts +2 -1
  335. package/dist/strategies/trailing-stop.js +52 -6
  336. package/dist/strategies/twap.d.ts +6 -1
  337. package/dist/strategies/twap.js +39 -11
  338. package/dist/trade-validator.d.ts +22 -0
  339. package/dist/trade-validator.js +119 -15
  340. package/dist/utils/numeric.d.ts +46 -0
  341. package/dist/utils/numeric.js +79 -0
  342. package/dist/utils.d.ts +19 -0
  343. package/dist/utils.js +96 -19
  344. package/dist/withdraw-gate.d.ts +49 -0
  345. package/dist/withdraw-gate.js +131 -0
  346. package/package.json +15 -6
  347. package/skills/perp-cli/SKILL.md +142 -55
  348. package/skills/perp-cli/references/agent-operations.md +110 -56
  349. package/skills/perp-cli/references/commands.md +266 -71
  350. package/skills/perp-cli/references/strategies.md +34 -31
  351. package/skills/perp-cli/scripts/arb-monitor.sh +10 -5
  352. package/skills/perp-cli/scripts/funding-analysis.sh +14 -9
  353. package/skills/perp-cli/scripts/preflight.sh +21 -14
  354. package/skills/perp-cli/scripts/spot-perp-scan.sh +12 -7
  355. package/skills/perp-cli/scripts/validate-arb.sh +31 -15
  356. package/vendor/lighter-signer/SHA256SUMS +2 -0
  357. package/vendor/lighter-signer/VERSION +8 -0
  358. package/vendor/lighter-signer/lighter-signer.wasm +0 -0
  359. package/vendor/lighter-signer/wasm_exec.js +575 -0
  360. package/dist/bot/strategies/funding-arb-v2-strategy.d.ts +0 -42
  361. package/dist/bot/strategies/funding-arb-v2-strategy.js +0 -864
@@ -16,6 +16,8 @@ export async function startEventStream(adapter, opts) {
16
16
  let prevOrders = new Map();
17
17
  let prevBalance = null;
18
18
  let cycle = 0;
19
+ // One-time notice per stream (one adapter = one venue) when the venue reports no liquidation price.
20
+ let warnedNoLiquidationPrice = false;
19
21
  const poll = async () => {
20
22
  cycle++;
21
23
  const ts = new Date().toISOString();
@@ -66,9 +68,33 @@ export async function startEventStream(adapter, opts) {
66
68
  }
67
69
  // ── Liquidation warnings ──
68
70
  for (const p of positions) {
71
+ // No liquidation price from the venue → no distance to measure. Checked
72
+ // before Number() because Number(null) === 0 would look like a real price.
73
+ if (p.liquidationPrice === null) {
74
+ if (!warnedNoLiquidationPrice) {
75
+ warnedNoLiquidationPrice = true;
76
+ console.warn(`[event-stream] ${adapter.name} does not report liquidation prices (first: ${p.symbol}) — liquidation warnings are unavailable for this venue`);
77
+ }
78
+ continue;
79
+ }
69
80
  const mark = Number(p.markPrice);
70
81
  const liq = Number(p.liquidationPrice);
71
- if (mark > 0 && liq > 0 && p.liquidationPrice !== "N/A") {
82
+ // Rule #2: a NaN mark or liq would silently fail the `> 0` checks
83
+ // (NaN comparisons are always false), suppressing the
84
+ // critical-distance liquidation_warning the user depends on.
85
+ // Surface the corruption explicitly so the stream layer doesn't
86
+ // hide a failed alert.
87
+ //
88
+ // Strict policy (qa/2026-05-16): an empty-string markPrice or
89
+ // liquidationPrice is also corruption — `Number("") === 0` would
90
+ // pass the `Number.isFinite` check, then `mark > 0` would be
91
+ // false, silently skipping the alert without surfacing the
92
+ // payload break. Add an explicit "" check to the rejection bucket.
93
+ if (p.markPrice === "" || p.liquidationPrice === "" || !Number.isFinite(mark) || !Number.isFinite(liq)) {
94
+ console.warn(`[event-stream] non-finite mark/liquidation for ${p.symbol} on ${adapter.name} (mark=${p.markPrice}, liq=${p.liquidationPrice}) — skipping liquidation distance check`);
95
+ continue;
96
+ }
97
+ if (mark > 0 && liq > 0) {
72
98
  const distancePct = Math.abs(mark - liq) / mark * 100;
73
99
  if (distancePct < 3) {
74
100
  emit({
@@ -121,22 +147,42 @@ export async function startEventStream(adapter, opts) {
121
147
  prevOrders = currentOrders;
122
148
  // ── Balance updates ──
123
149
  if (prevBalance) {
124
- const equityDelta = Math.abs(Number(balance.equity) - Number(prevBalance.equity));
125
- const availDelta = Math.abs(Number(balance.available) - Number(prevBalance.available));
126
- if (equityDelta > 0.01 || availDelta > 0.01) {
127
- emit({
128
- type: "balance_update",
129
- exchange: adapter.name,
130
- timestamp: ts,
131
- data: {
132
- equity: balance.equity,
133
- available: balance.available,
134
- marginUsed: balance.marginUsed,
135
- unrealizedPnl: balance.unrealizedPnl,
136
- prevEquity: prevBalance.equity,
137
- prevAvailable: prevBalance.available,
138
- },
139
- });
150
+ const equityNow = Number(balance.equity);
151
+ const equityPrev = Number(prevBalance.equity);
152
+ const availNow = Number(balance.available);
153
+ const availPrev = Number(prevBalance.available);
154
+ // Rule #2: a NaN delta would silently fail the `> 0.01` threshold,
155
+ // suppressing balance_update events. Surface the corruption.
156
+ //
157
+ // Strict policy (qa/2026-05-16): "" coerces to 0 via Number(""),
158
+ // so `Math.abs(0 - prev)` could pass the `> 0.01` check and emit
159
+ // a misleading balance_update with `data.equity = ""`. Reject ""
160
+ // upfront so the corruption is visible, not laundered into a
161
+ // synthetic delta event.
162
+ const empties = balance.equity === "" || prevBalance.equity === "" ||
163
+ balance.available === "" || prevBalance.available === "";
164
+ if (empties || !Number.isFinite(equityNow) || !Number.isFinite(equityPrev) ||
165
+ !Number.isFinite(availNow) || !Number.isFinite(availPrev)) {
166
+ console.warn(`[event-stream] non-finite balance values for ${adapter.name} (equity=${balance.equity}/${prevBalance.equity}, available=${balance.available}/${prevBalance.available}) — skipping balance_update emit`);
167
+ }
168
+ else {
169
+ const equityDelta = Math.abs(equityNow - equityPrev);
170
+ const availDelta = Math.abs(availNow - availPrev);
171
+ if (equityDelta > 0.01 || availDelta > 0.01) {
172
+ emit({
173
+ type: "balance_update",
174
+ exchange: adapter.name,
175
+ timestamp: ts,
176
+ data: {
177
+ equity: balance.equity,
178
+ available: balance.available,
179
+ marginUsed: balance.marginUsed,
180
+ unrealizedPnl: balance.unrealizedPnl,
181
+ prevEquity: prevBalance.equity,
182
+ prevAvailable: prevBalance.available,
183
+ },
184
+ });
185
+ }
140
186
  }
141
187
  }
142
188
  prevBalance = balance;
@@ -169,8 +215,18 @@ export async function startEventStream(adapter, opts) {
169
215
  // Polling loop
170
216
  while (!opts.signal?.aborted) {
171
217
  await new Promise((resolve) => {
172
- const timer = setTimeout(resolve, intervalMs);
173
- opts.signal?.addEventListener("abort", () => { clearTimeout(timer); resolve(); }, { once: true });
218
+ const signal = opts.signal;
219
+ let timer;
220
+ const onAbort = () => { clearTimeout(timer); resolve(); };
221
+ // When the timer wins the race, remove the abort listener so it does not
222
+ // accumulate on the (long-lived) signal across polling iterations.
223
+ // When abort wins, { once: true } removes it. Either way the signal is
224
+ // left with zero listeners from this iteration — no MaxListeners leak.
225
+ timer = setTimeout(() => {
226
+ signal?.removeEventListener("abort", onAbort);
227
+ resolve();
228
+ }, intervalMs);
229
+ signal?.addEventListener("abort", onAbort, { once: true });
174
230
  });
175
231
  if (opts.signal?.aborted)
176
232
  break;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * On-chain EVM transactions sent by the master wallet (Aster vault deposit, Ondo funding
3
+ * transfer, Relay bridge deposits).
4
+ *
5
+ * One path for all of them: simulate (eth_estimateGas from the sender) → gas balance check →
6
+ * sign through a transaction signer (the digest is derived from the unsigned tx inside the
7
+ * signer — no raw-digest API) → verify the recovered sender → broadcast → wait for the receipt.
8
+ *
9
+ * Unknown outcomes are never reported as failures: once the signed transaction left this
10
+ * process, a lost broadcast response, an RPC error while waiting, or a receipt timeout is
11
+ * ORDER_PENDING carrying the transaction hash ("do NOT resend").
12
+ */
13
+ import { Interface, JsonRpcProvider, Transaction, type TransactionReceipt } from "ethers";
14
+ import { PerpError } from "./errors.js";
15
+ import type { CliCredentialOpts } from "./config.js";
16
+ import type { VenueId } from "./exchanges/venue-types.js";
17
+ import type { OwsEvmSigner } from "./signer/ows-evm.js";
18
+ /** Signs an unsigned tx (hashing internally) and returns the 65-byte 0x signature. */
19
+ export type TxSigner = (tx: Transaction) => Promise<string>;
20
+ export declare const EVM_RECEIPT_TIMEOUT_MS = 180000;
21
+ export declare const ERC20: Interface;
22
+ export declare const EVM_ADDRESS_RE: RegExp;
23
+ export declare function sameAddress(a: string, b: string): boolean;
24
+ export declare function evmProvider(rpcUrl: string, chainId: number): JsonRpcProvider;
25
+ /** The RPC must serve the pinned chain — a wrong endpoint never receives a signed transaction. */
26
+ export declare function assertRpcChainId(rpc: JsonRpcProvider, rpcUrl: string, chainId: number, venue: string): Promise<void>;
27
+ /** On-chain decimals() — never assumed from a table. */
28
+ export declare function erc20Decimals(rpc: JsonRpcProvider, token: string): Promise<number>;
29
+ export declare function erc20Balance(rpc: JsonRpcProvider, token: string, owner: string): Promise<bigint>;
30
+ export declare function erc20Allowance(rpc: JsonRpcProvider, token: string, owner: string, spender: string): Promise<bigint>;
31
+ export interface MasterTx {
32
+ /** Venue / provider name for error attribution ("aster", "ondo", "relay"). */
33
+ venue: string;
34
+ /** Human chain name for messages ("BNB Chain"). */
35
+ chainLabel: string;
36
+ chainId: number;
37
+ from: string;
38
+ to: string;
39
+ data: string;
40
+ value: bigint;
41
+ /** What this transaction is, for messages ("USDT approve"). */
42
+ label: string;
43
+ }
44
+ /** A sent transaction whose result could not be read: unknown outcome carrying the hash. */
45
+ export declare function txOutcomeUnknown(t: MasterTx, txHash: string, why: string): PerpError;
46
+ /**
47
+ * Simulate, sign, broadcast one master-wallet transaction and return its hash + successful receipt.
48
+ * Throws before signing when the simulation reverts or gas is short (nothing sent); a reverted
49
+ * receipt is ACTION_REJECTED (definite); anything unreadable after the broadcast is ORDER_PENDING.
50
+ */
51
+ export declare function sendMasterTx(rpc: JsonRpcProvider, t: MasterTx, sign: TxSigner): Promise<{
52
+ txHash: string;
53
+ receipt: TransactionReceipt;
54
+ }>;
55
+ /**
56
+ * The OWS master wallet as a transaction signer. On-chain master transactions are signed only by
57
+ * an OWS wallet (digest derived inside the signer); no master / a raw private key → NO_SIGNER_AVAILABLE.
58
+ */
59
+ export declare function loadMasterEvmSigner(venue: VenueId, opts: CliCredentialOpts, action: string): Promise<OwsEvmSigner>;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * On-chain EVM transactions sent by the master wallet (Aster vault deposit, Ondo funding
3
+ * transfer, Relay bridge deposits).
4
+ *
5
+ * One path for all of them: simulate (eth_estimateGas from the sender) → gas balance check →
6
+ * sign through a transaction signer (the digest is derived from the unsigned tx inside the
7
+ * signer — no raw-digest API) → verify the recovered sender → broadcast → wait for the receipt.
8
+ *
9
+ * Unknown outcomes are never reported as failures: once the signed transaction left this
10
+ * process, a lost broadcast response, an RPC error while waiting, or a receipt timeout is
11
+ * ORDER_PENDING carrying the transaction hash ("do NOT resend").
12
+ */
13
+ import { Interface, JsonRpcProvider, Network as EthersNetwork, Signature, Transaction } from "ethers";
14
+ import { PerpError, isConnectFailure } from "./errors.js";
15
+ export const EVM_RECEIPT_TIMEOUT_MS = 180_000;
16
+ /** Gas-limit headroom over eth_estimateGas. */
17
+ const GAS_HEADROOM_PCT = 20n;
18
+ export const ERC20 = new Interface([
19
+ "function decimals() view returns (uint8)",
20
+ "function balanceOf(address) view returns (uint256)",
21
+ "function allowance(address owner, address spender) view returns (uint256)",
22
+ "function approve(address spender, uint256 amount) returns (bool)",
23
+ "function transfer(address to, uint256 amount) returns (bool)",
24
+ ]);
25
+ export const EVM_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
26
+ export function sameAddress(a, b) {
27
+ return a.toLowerCase() === b.toLowerCase();
28
+ }
29
+ export function evmProvider(rpcUrl, chainId) {
30
+ const net = EthersNetwork.from(chainId);
31
+ return new JsonRpcProvider(rpcUrl, net, { staticNetwork: net });
32
+ }
33
+ /** The RPC must serve the pinned chain — a wrong endpoint never receives a signed transaction. */
34
+ export async function assertRpcChainId(rpc, rpcUrl, chainId, venue) {
35
+ const served = BigInt(await rpc.send("eth_chainId", []));
36
+ if (served !== BigInt(chainId)) {
37
+ throw new PerpError("EXCHANGE_ERROR", `${venue}: RPC ${rpcUrl} serves chainId ${served}, expected ${chainId} — nothing was sent`, { exchange: venue });
38
+ }
39
+ }
40
+ /** On-chain decimals() — never assumed from a table. */
41
+ export async function erc20Decimals(rpc, token) {
42
+ const raw = await rpc.call({ to: token, data: ERC20.encodeFunctionData("decimals", []) });
43
+ return Number(ERC20.decodeFunctionResult("decimals", raw)[0]);
44
+ }
45
+ export async function erc20Balance(rpc, token, owner) {
46
+ const raw = await rpc.call({ to: token, data: ERC20.encodeFunctionData("balanceOf", [owner]) });
47
+ return ERC20.decodeFunctionResult("balanceOf", raw)[0];
48
+ }
49
+ export async function erc20Allowance(rpc, token, owner, spender) {
50
+ const raw = await rpc.call({ to: token, data: ERC20.encodeFunctionData("allowance", [owner, spender]) });
51
+ return ERC20.decodeFunctionResult("allowance", raw)[0];
52
+ }
53
+ /** A sent transaction whose result could not be read: unknown outcome carrying the hash. */
54
+ export function txOutcomeUnknown(t, txHash, why) {
55
+ return new PerpError("ORDER_PENDING", `${t.venue} ${t.label}: transaction ${txHash} was sent on ${t.chainLabel} but its result is unknown — ${why}`, {
56
+ exchange: t.venue, txHash, chainId: t.chainId,
57
+ remediation: `Do NOT resend — the transaction may still confirm. Check ${txHash} on a ${t.chainLabel} explorer, then the balances, before doing anything else`,
58
+ });
59
+ }
60
+ /**
61
+ * Simulate, sign, broadcast one master-wallet transaction and return its hash + successful receipt.
62
+ * Throws before signing when the simulation reverts or gas is short (nothing sent); a reverted
63
+ * receipt is ACTION_REJECTED (definite); anything unreadable after the broadcast is ORDER_PENDING.
64
+ */
65
+ export async function sendMasterTx(rpc, t, sign) {
66
+ let estimate;
67
+ try {
68
+ estimate = await rpc.estimateGas({ from: t.from, to: t.to, data: t.data, value: t.value });
69
+ }
70
+ catch (e) {
71
+ throw new PerpError("ACTION_REJECTED", `${t.venue} ${t.label}: simulation on ${t.chainLabel} failed (${e instanceof Error ? e.message : String(e)}) — nothing was sent`, {
72
+ exchange: t.venue, chainId: t.chainId,
73
+ });
74
+ }
75
+ const gasLimit = estimate + (estimate * GAS_HEADROOM_PCT) / 100n;
76
+ const fee = await rpc.getFeeData();
77
+ // EIP-1559 chains price with maxFee / priority; a chain whose blocks carry no base fee (BNB Chain
78
+ // reports baseFeePerGas 0, so ethers returns maxFeePerGas null) is priced with a legacy gasPrice.
79
+ const eip1559 = fee.maxFeePerGas !== null && fee.maxPriorityFeePerGas !== null;
80
+ if (!eip1559 && fee.gasPrice === null) {
81
+ throw new PerpError("EXCHANGE_ERROR", `${t.venue}: the ${t.chainLabel} RPC returned no fee data — nothing was sent`, { exchange: t.venue });
82
+ }
83
+ const pricePerGas = eip1559 ? fee.maxFeePerGas : fee.gasPrice;
84
+ const need = gasLimit * pricePerGas + t.value;
85
+ const native = await rpc.getBalance(t.from);
86
+ if (native < need) {
87
+ throw new PerpError("INSUFFICIENT_BALANCE", `${t.label} needs up to ${need} wei of ${t.chainLabel} native token (gas${t.value > 0n ? " + value" : ""}); wallet ${t.from} has ${native}`, {
88
+ exchange: t.venue, remediation: `Fund ${t.from} with native gas token on ${t.chainLabel} (chainId ${t.chainId}), then retry`,
89
+ });
90
+ }
91
+ const nonce = await rpc.getTransactionCount(t.from, "pending");
92
+ const common = { chainId: BigInt(t.chainId), nonce, to: t.to, data: t.data, value: t.value, gasLimit };
93
+ const tx = eip1559
94
+ ? Transaction.from({ ...common, type: 2, maxFeePerGas: fee.maxFeePerGas, maxPriorityFeePerGas: fee.maxPriorityFeePerGas })
95
+ : Transaction.from({ ...common, type: 0, gasPrice: fee.gasPrice });
96
+ tx.signature = Signature.from(await sign(tx));
97
+ if (tx.from === null || !sameAddress(tx.from, t.from)) {
98
+ throw new PerpError("SIGNATURE_FAILED", `${t.label} tx recovered signer ${tx.from}, expected ${t.from} — nothing was sent`, { exchange: t.venue });
99
+ }
100
+ const txHash = tx.hash;
101
+ if (txHash === null)
102
+ throw new Error(`${t.label}: signed transaction has no hash`);
103
+ try {
104
+ // The raw RPC call only: ethers' broadcastTransaction also reads the block number and
105
+ // re-checks the hash, so it can throw after the node already accepted the transaction.
106
+ await rpc.send("eth_sendRawTransaction", [tx.serialized]);
107
+ }
108
+ catch (e) {
109
+ // No connection at all → definitely not sent. "insufficient funds" → the node refused it.
110
+ // Anything else (timeout, reset, 5xx, "already known", "nonce too low" from a proxy that
111
+ // retried) may mean the transaction IS in the mempool: wait for the hash below.
112
+ if (isConnectFailure(e)) {
113
+ throw new PerpError("EXCHANGE_UNREACHABLE", `${t.venue} ${t.label}: could not connect to the ${t.chainLabel} RPC — nothing was sent`, { exchange: t.venue });
114
+ }
115
+ if (e.code === "INSUFFICIENT_FUNDS") {
116
+ throw new PerpError("ACTION_REJECTED", `${t.venue} ${t.label}: the ${t.chainLabel} RPC refused the transaction (${e instanceof Error ? e.message : String(e)}) — nothing was sent`, {
117
+ exchange: t.venue, chainId: t.chainId,
118
+ });
119
+ }
120
+ }
121
+ let receipt;
122
+ try {
123
+ receipt = await rpc.waitForTransaction(txHash, 1, EVM_RECEIPT_TIMEOUT_MS);
124
+ }
125
+ catch (e) {
126
+ throw txOutcomeUnknown(t, txHash, e.code === "TIMEOUT"
127
+ ? `not mined within ${EVM_RECEIPT_TIMEOUT_MS / 1000}s`
128
+ : `receipt read failed (${e instanceof Error ? e.message : String(e)})`);
129
+ }
130
+ if (receipt === null)
131
+ throw txOutcomeUnknown(t, txHash, `not mined within ${EVM_RECEIPT_TIMEOUT_MS / 1000}s`);
132
+ if (receipt.status !== 1) {
133
+ throw new PerpError("ACTION_REJECTED", `${t.venue} ${t.label}: transaction ${txHash} reverted on ${t.chainLabel} — nothing was moved`, {
134
+ exchange: t.venue, txHash, chainId: t.chainId,
135
+ });
136
+ }
137
+ return { txHash, receipt };
138
+ }
139
+ /**
140
+ * The OWS master wallet as a transaction signer. On-chain master transactions are signed only by
141
+ * an OWS wallet (digest derived inside the signer); no master / a raw private key → NO_SIGNER_AVAILABLE.
142
+ */
143
+ export async function loadMasterEvmSigner(venue, opts, action) {
144
+ const { resolveOwsMaster } = await import("./config.js");
145
+ const master = await resolveOwsMaster(venue, opts);
146
+ if (master === null) {
147
+ throw new PerpError("NO_SIGNER_AVAILABLE", `${action} is an on-chain transaction signed by the OWS master wallet, which is not selected on this computer`, {
148
+ exchange: venue,
149
+ remediation: "Import the master wallet into the OWS vault (perp wallet import --name <name> --chain evm), then perp wallet use <name> or pass --ows <name>",
150
+ });
151
+ }
152
+ const { OwsEvmSigner: Signer } = await import("./signer/ows-evm.js");
153
+ return Signer.create(master.wallet, master.passphrase);
154
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The network an adapter instance was created for. Legacy adapters keep it in private fields of
3
+ * different shapes, so the creator registers it here (cli-program createVenueAdapter, MCP
4
+ * getAdapter); TradingV2 adapters also expose `network` themselves. Unknown → null (callers that
5
+ * need it refuse; never a guessed network).
6
+ */
7
+ import type { ExchangeAdapter } from "./interface.js";
8
+ import type { Network } from "./venue-types.js";
9
+ export declare function registerAdapterNetwork(adapter: ExchangeAdapter, network: Network): void;
10
+ export declare function adapterNetworkOf(adapter: ExchangeAdapter): Network | null;
@@ -0,0 +1,10 @@
1
+ const networks = new WeakMap();
2
+ export function registerAdapterNetwork(adapter, network) {
3
+ networks.set(adapter, network);
4
+ }
5
+ export function adapterNetworkOf(adapter) {
6
+ const own = adapter.network;
7
+ if (own === "mainnet" || own === "testnet")
8
+ return own;
9
+ return networks.get(adapter) ?? null;
10
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Arcus adapter — native TradingV2 + legacy ExchangeAdapter shim (design §10.7).
3
+ *
4
+ * Signing: the agent API key is an Ed25519 key in OWS wallet
5
+ * `meta.agentWalletName` (raw signMessage on the Solana account — no prefix).
6
+ * Account = meta.ownerAddress, subaccount = meta.accountIndex. Without an agent
7
+ * (master creds only, for spot swaps) every perp private call throws
8
+ * AGENT_NOT_REGISTERED from requireAccount().
9
+ *
10
+ * Order entry rules (docs.arcus.xyz, design §10.10):
11
+ * - every order carries goodTilTime ≥ 1 month ahead (ARCUS_GOOD_TIL_HORIZON_MS),
12
+ * body in µs, signed `g` in ns;
13
+ * - market orders = IOC LIMIT at a protection price derived from mark and an
14
+ * explicit slippage bound, rejected locally when > 10 % from mark;
15
+ * - TIF (design §13.3): common GTC is sent as GTT with goodTil = now +
16
+ * ARCUS_GOOD_TIL_HORIZON_MS (recorded as execution.expiresAt); IOC / FOK / ALO
17
+ * are native; a limit order without a TIF is GTC;
18
+ * - clientId is always sent (orderAck async) and every OrderResult from a
19
+ * placement is `accepted` / `pending` unless the gateway already had state;
20
+ * - stop / TP-SL orders exist only as batchPlaceOrders groupings: a standalone
21
+ * stop is a 1-leg `partialTpsl` (reduce-only), TP/SL attached to an entry is
22
+ * an `entryTpsl` bundle.
23
+ */
24
+ import type { EvmSigner } from "../../signer/interface.js";
25
+ import type { ExchangeAdapter, ExchangeBalance, ExchangeFundingPayment, ExchangeKline, ExchangeMarketInfo, ExchangeOrder, ExchangePosition, ExchangeTrade } from "../interface.js";
26
+ import type { ResolvedSlippage } from "../slippage.js";
27
+ import { type TradingV2 } from "../trading-v2.js";
28
+ import type { VenueCreateContext, VenueDefinition } from "../venue.js";
29
+ import { type AmendRequest, type CancelResult, type LeverageResult, type Network, type OrderRef, type OrderRequest, type OrderResult, type VenueMarket } from "../venue-types.js";
30
+ /**
31
+ * goodTilTime horizon. Arcus rejects anything nearer than one month at
32
+ * handling time (31 days covers every calendar month); the docs' examples use
33
+ * now + 40 days, which also leaves headroom for clock skew.
34
+ */
35
+ export declare const ARCUS_GOOD_TIL_HORIZON_MS: number;
36
+ /** Arcus rejects MARKET protection prices more than 10 % from mark (also enforced locally for IOC limits). */
37
+ export declare const ARCUS_MAX_PROTECTION_DEVIATION = 0.1;
38
+ /** clientId max (36) minus the "-tp"/"-sl" suffix of entryTpsl child legs. */
39
+ export declare const ARCUS_TPSL_CLIENT_ID_MAX = 33;
40
+ export declare class ArcusAdapter implements ExchangeAdapter, TradingV2 {
41
+ readonly name: "arcus";
42
+ readonly venue: VenueDefinition;
43
+ readonly network: Network;
44
+ private readonly caps;
45
+ private readonly client;
46
+ private readonly account;
47
+ private readonly masterCreds;
48
+ private readonly masterPk;
49
+ private markets;
50
+ constructor(def: VenueDefinition, ctx: VenueCreateContext);
51
+ /**
52
+ * Single guard for every perp private call (orders / cancel / amend /
53
+ * leverage / private reads / withdraw): they all need the Ed25519 agent key.
54
+ * Master-only adapters (spot swaps) and read-only adapters land here.
55
+ */
56
+ private requireAccount;
57
+ private accountQuery;
58
+ private loadMarkets;
59
+ private market;
60
+ private symbolOf;
61
+ /** Protection price: slippage from `ref`, aligned toward crossing on the tier tick, ≤ 10 % from `ref`. */
62
+ private protectionPrice;
63
+ /** Limit price must sit on its tier's tick; the signed integer always uses the top-level tickSize. */
64
+ private limitTicks;
65
+ private leg;
66
+ private signLeg;
67
+ /**
68
+ * Placement / modify acknowledgement → OrderResult.
69
+ *
70
+ * filledSize (M9): on the 202 path the body is an ACK "carrying no terminal
71
+ * state" and the enrichment fields (filledSize, remainingSize) are omitted
72
+ * (openapi OrderResponse / BatchOrderItemResponse / ModifyOrderResponse). An
73
+ * ACK has not been matched yet, so it has no fills by definition: "0" is used
74
+ * ONLY for status "accepted" (finality pending). On the 200 path the fill is
75
+ * read from filledSize, else derived as quantity − remainingSize (the docs
76
+ * define filledSize = originalSize − remainingSize); otherwise it is unknown
77
+ * and EXCHANGE_ERROR is thrown (the order exists — do not retry blindly).
78
+ */
79
+ private resultFromPlace;
80
+ /** Order shape: filledSize is optional and documented as originalSize − remainingSize (both required). */
81
+ private filledOf;
82
+ private orderResultFromRaw;
83
+ /** TP/SL classification of a reduce-only trigger relative to the current mark. */
84
+ private tpslType;
85
+ private findOrder;
86
+ getVenueMarkets(): Promise<VenueMarket[]>;
87
+ placeOrder(input: OrderRequest): Promise<OrderResult>;
88
+ /** Size flooring is surfaced, never silent (L2): wrap venueRaw with requested/placed sizes. */
89
+ private withFloorNote;
90
+ /** batchPlaceOrders; returns the first leg's result, throws if any leg failed validation. */
91
+ private sendBatch;
92
+ /**
93
+ * modifyOrder (docs modify-order / authentication).
94
+ *
95
+ * Size: the CLI's `req.size` is the new OPEN (unfilled) size (design §13.4), while
96
+ * Arcus modifies take the order's new TOTAL size, filled quantity included
97
+ * (OrderRejectionReason MODIFY_SIZE_ALREADY_FILLED: "the order's new TOTAL size,
98
+ * filled quantity included"). So body `quantity` and signed `q` are
99
+ * filledSize (as read just before) + new open size. A new open size ≤ 0 is a
100
+ * cancel, not a modify. A fill that races the modify shrinks the resting
101
+ * remainder; if fills reach the new total the venue cancels the order and rejects
102
+ * with MODIFY_SIZE_ALREADY_FILLED — reported as the order's read-back (cancelled) state.
103
+ *
104
+ * Identity: the body carries `orderId` only (exactly one of orderId / clientId);
105
+ * the signed payload follows ARCUS_MODIFY_SIGNED_IDENTITY (UNVERIFIED, signing.ts).
106
+ * g/r/s/t echo the resting order; `clientTime` = signed `ct` = X-Timestamp.
107
+ */
108
+ amendOrder(req: AmendRequest): Promise<OrderResult>;
109
+ /**
110
+ * A modify ack (ACK / OPEN) is not the edited order's state: read the order back
111
+ * (GET /v1/order/{id}, bounded 5 × 1 s). Final when it is terminal (filled / cancelled /
112
+ * rejected), or resting with the new price and total size; otherwise the ack stays pending.
113
+ */
114
+ private confirmModify;
115
+ /**
116
+ * MODIFY_SIZE_ALREADY_FILLED: fills reached the new total, so the venue cancelled the
117
+ * original order. Report the order's read-back state (cancelled, with its real fills).
118
+ */
119
+ private modifyAlreadyFilled;
120
+ cancelByRef(symbol: string, ref: OrderRef): Promise<CancelResult>;
121
+ /**
122
+ * Every open order (GET /v1/openOrders pages hold ≤ 1000 rows, newest first; the next
123
+ * page is `to` = the oldest `createdAt` seen, inclusive → dedupe by orderId).
124
+ */
125
+ private allOpenOrders;
126
+ cancelAllV2(symbol: string | null): Promise<CancelResult>;
127
+ applyLeverage(symbol: string, leverage: number, marginMode: "cross" | "isolated" | null): Promise<LeverageResult>;
128
+ /**
129
+ * Per-market leverage / margin mode: GET /v1/leverages (docs.arcus.xyz openapi getLeverages:
130
+ * "Returns the effective leverage and margin mode (CROSS/ISOLATED) for `(address,
131
+ * accountIndex)` across every market … Each entry reflects the user's leverage override (set
132
+ * via `POST /v1/setLeverage`) when one exists, otherwise the market default"; LeverageEntry
133
+ * requires marketId, marketDisplayName, leverage (integer ≥ 1), isolated, marginMode).
134
+ */
135
+ getMarketSettings(): Promise<Array<{
136
+ symbol: string;
137
+ leverage: number;
138
+ marginMode: "cross" | "isolated";
139
+ }>>;
140
+ getOrderStatus(symbol: string, ref: OrderRef): Promise<OrderResult>;
141
+ getMarkets(): Promise<ExchangeMarketInfo[]>;
142
+ getOrderbook(symbol: string): Promise<{
143
+ bids: [string, string][];
144
+ asks: [string, string][];
145
+ }>;
146
+ getRecentTrades(symbol: string, limit?: number): Promise<ExchangeTrade[]>;
147
+ getFundingHistory(symbol: string, limit?: number): Promise<{
148
+ time: number;
149
+ rate: string;
150
+ price: string | null;
151
+ }[]>;
152
+ getKlines(symbol: string, interval: string, startTime: number, endTime: number): Promise<ExchangeKline[]>;
153
+ private rawPositions;
154
+ getBalance(): Promise<ExchangeBalance>;
155
+ getPositions(): Promise<ExchangePosition[]>;
156
+ private toLegacyOrders;
157
+ getOpenOrders(): Promise<ExchangeOrder[]>;
158
+ getOrderHistory(limit?: number): Promise<ExchangeOrder[]>;
159
+ getTradeHistory(limit?: number): Promise<ExchangeTrade[]>;
160
+ getFundingPayments(limit?: number): Promise<ExchangeFundingPayment[]>;
161
+ /** `clientOrderId`: the caller's id when given; otherwise one is generated (orderAck async needs one). */
162
+ marketOrder(symbol: string, side: "buy" | "sell", size: string, opts: {
163
+ reduceOnly: boolean;
164
+ slippage: ResolvedSlippage;
165
+ clientOrderId?: string;
166
+ }): Promise<unknown>;
167
+ limitOrder(symbol: string, side: "buy" | "sell", price: string, size: string, opts?: {
168
+ reduceOnly?: boolean;
169
+ tif?: string;
170
+ clientOrderId?: string;
171
+ }): Promise<unknown>;
172
+ editOrder(symbol: string, orderId: string, price: string, size: string): Promise<unknown>;
173
+ cancelOrder(symbol: string, orderId: string): Promise<unknown>;
174
+ cancelAllOrders(symbol?: string): Promise<unknown>;
175
+ setLeverage(symbol: string, leverage: number, marginMode?: "cross" | "isolated"): Promise<unknown>;
176
+ /** `opts.slippage` (resolveSlippage() output) is required for a stop-market: its leg is protection-priced at trigger ± bound. */
177
+ stopOrder(symbol: string, side: "buy" | "sell", size: string, triggerPrice: string, opts?: {
178
+ limitPrice?: string;
179
+ reduceOnly?: boolean;
180
+ triggerType?: "mark" | "last" | "mid";
181
+ slippage?: ResolvedSlippage;
182
+ }): Promise<unknown>;
183
+ private masterSigner;
184
+ private requireMasterKey;
185
+ /** Master EVM signer for on-chain actions outside the Arcus API (spot swaps, design §12.4 #7). */
186
+ masterEvmSigner(): Promise<EvmSigner>;
187
+ /** Sign an unsigned EVM transaction with the master key (digest derived here). Returns a 65-byte 0x signature. */
188
+ signMasterTransaction(tx: import("ethers").Transaction): Promise<string>;
189
+ /** The only valid withdraw destination: the account owner (needs the agent API key). */
190
+ get withdrawDestination(): `0x${string}`;
191
+ /** Withdraw-to-self (Arcus v0 has no separate recipient). `amount` in USDG. */
192
+ withdraw(amount: string, destination: string, _opts?: {
193
+ assetId?: number;
194
+ routeType?: number;
195
+ }): Promise<unknown>;
196
+ }