perp-cli 0.3.3

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 (325) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +293 -0
  3. package/dist/__tests__/alert-logic.test.d.ts +1 -0
  4. package/dist/__tests__/alert-logic.test.js +107 -0
  5. package/dist/__tests__/arb-auto-3dex.test.d.ts +1 -0
  6. package/dist/__tests__/arb-auto-3dex.test.js +397 -0
  7. package/dist/__tests__/arb-history-stats.test.d.ts +1 -0
  8. package/dist/__tests__/arb-history-stats.test.js +176 -0
  9. package/dist/__tests__/arb-logic.test.d.ts +1 -0
  10. package/dist/__tests__/arb-logic.test.js +84 -0
  11. package/dist/__tests__/arb-manage.test.d.ts +1 -0
  12. package/dist/__tests__/arb-manage.test.js +253 -0
  13. package/dist/__tests__/arb-new-features.test.d.ts +1 -0
  14. package/dist/__tests__/arb-new-features.test.js +457 -0
  15. package/dist/__tests__/arb-sizing.test.d.ts +1 -0
  16. package/dist/__tests__/arb-sizing.test.js +48 -0
  17. package/dist/__tests__/arb-state.test.d.ts +1 -0
  18. package/dist/__tests__/arb-state.test.js +284 -0
  19. package/dist/__tests__/arb-userflow.test.d.ts +1 -0
  20. package/dist/__tests__/arb-userflow.test.js +945 -0
  21. package/dist/__tests__/arb-utils.test.d.ts +1 -0
  22. package/dist/__tests__/arb-utils.test.js +264 -0
  23. package/dist/__tests__/bot-conditions.test.d.ts +1 -0
  24. package/dist/__tests__/bot-conditions.test.js +341 -0
  25. package/dist/__tests__/client-id-tracker.test.d.ts +1 -0
  26. package/dist/__tests__/client-id-tracker.test.js +137 -0
  27. package/dist/__tests__/commands/new-atomic-commands.test.d.ts +1 -0
  28. package/dist/__tests__/commands/new-atomic-commands.test.js +502 -0
  29. package/dist/__tests__/commands/order-intent.test.d.ts +1 -0
  30. package/dist/__tests__/commands/order-intent.test.js +600 -0
  31. package/dist/__tests__/commands/trade-commands.test.d.ts +1 -0
  32. package/dist/__tests__/commands/trade-commands.test.js +821 -0
  33. package/dist/__tests__/config.test.d.ts +1 -0
  34. package/dist/__tests__/config.test.js +86 -0
  35. package/dist/__tests__/cross-chain-margin.test.d.ts +1 -0
  36. package/dist/__tests__/cross-chain-margin.test.js +287 -0
  37. package/dist/__tests__/dex-asset-map.test.d.ts +1 -0
  38. package/dist/__tests__/dex-asset-map.test.js +191 -0
  39. package/dist/__tests__/errors.test.d.ts +1 -0
  40. package/dist/__tests__/errors.test.js +110 -0
  41. package/dist/__tests__/event-stream.test.d.ts +1 -0
  42. package/dist/__tests__/event-stream.test.js +276 -0
  43. package/dist/__tests__/exchanges/interface.test.d.ts +1 -0
  44. package/dist/__tests__/exchanges/interface.test.js +132 -0
  45. package/dist/__tests__/exchanges/mock-adapter.d.ts +69 -0
  46. package/dist/__tests__/exchanges/mock-adapter.js +137 -0
  47. package/dist/__tests__/execution-log.test.d.ts +1 -0
  48. package/dist/__tests__/execution-log.test.js +106 -0
  49. package/dist/__tests__/funding-calc.test.d.ts +1 -0
  50. package/dist/__tests__/funding-calc.test.js +71 -0
  51. package/dist/__tests__/funding-history.test.d.ts +1 -0
  52. package/dist/__tests__/funding-history.test.js +343 -0
  53. package/dist/__tests__/funding-rates.test.d.ts +1 -0
  54. package/dist/__tests__/funding-rates.test.js +342 -0
  55. package/dist/__tests__/funding.test.d.ts +1 -0
  56. package/dist/__tests__/funding.test.js +173 -0
  57. package/dist/__tests__/gap-logic.test.d.ts +1 -0
  58. package/dist/__tests__/gap-logic.test.js +43 -0
  59. package/dist/__tests__/hip3-dex.test.d.ts +1 -0
  60. package/dist/__tests__/hip3-dex.test.js +234 -0
  61. package/dist/__tests__/integration/agent-features.integration.test.d.ts +1 -0
  62. package/dist/__tests__/integration/agent-features.integration.test.js +553 -0
  63. package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +13 -0
  64. package/dist/__tests__/integration/atomic-commands.integration.test.js +246 -0
  65. package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +1 -0
  66. package/dist/__tests__/integration/bridge-simulation.integration.test.js +453 -0
  67. package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +1 -0
  68. package/dist/__tests__/integration/bridge-strict.integration.test.js +812 -0
  69. package/dist/__tests__/integration/bridge.integration.test.d.ts +1 -0
  70. package/dist/__tests__/integration/bridge.integration.test.js +309 -0
  71. package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +1 -0
  72. package/dist/__tests__/integration/cli-e2e.integration.test.js +202 -0
  73. package/dist/__tests__/integration/dex-arb.integration.test.d.ts +1 -0
  74. package/dist/__tests__/integration/dex-arb.integration.test.js +116 -0
  75. package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +13 -0
  76. package/dist/__tests__/integration/envelope-consistency.integration.test.js +205 -0
  77. package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +1 -0
  78. package/dist/__tests__/integration/hip3-dex.integration.test.js +147 -0
  79. package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +1 -0
  80. package/dist/__tests__/integration/hyperliquid.integration.test.js +79 -0
  81. package/dist/__tests__/integration/lighter.integration.test.d.ts +1 -0
  82. package/dist/__tests__/integration/lighter.integration.test.js +53 -0
  83. package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +9 -0
  84. package/dist/__tests__/integration/new-commands-e2e.integration.test.js +236 -0
  85. package/dist/__tests__/integration/order-verification.integration.test.d.ts +1 -0
  86. package/dist/__tests__/integration/order-verification.integration.test.js +321 -0
  87. package/dist/__tests__/integration/pacifica.integration.test.d.ts +1 -0
  88. package/dist/__tests__/integration/pacifica.integration.test.js +75 -0
  89. package/dist/__tests__/integration/response-shapes.integration.test.d.ts +1 -0
  90. package/dist/__tests__/integration/response-shapes.integration.test.js +278 -0
  91. package/dist/__tests__/liquidity.test.d.ts +1 -0
  92. package/dist/__tests__/liquidity.test.js +225 -0
  93. package/dist/__tests__/plan-executor.test.d.ts +1 -0
  94. package/dist/__tests__/plan-executor.test.js +314 -0
  95. package/dist/__tests__/position-history.test.d.ts +1 -0
  96. package/dist/__tests__/position-history.test.js +367 -0
  97. package/dist/__tests__/retry.test.d.ts +1 -0
  98. package/dist/__tests__/retry.test.js +310 -0
  99. package/dist/__tests__/risk-assessment.test.d.ts +1 -0
  100. package/dist/__tests__/risk-assessment.test.js +145 -0
  101. package/dist/__tests__/security-adversarial.test.d.ts +1 -0
  102. package/dist/__tests__/security-adversarial.test.js +574 -0
  103. package/dist/__tests__/strategies.test.d.ts +1 -0
  104. package/dist/__tests__/strategies.test.js +539 -0
  105. package/dist/__tests__/trade-execution.test.d.ts +1 -0
  106. package/dist/__tests__/trade-execution.test.js +129 -0
  107. package/dist/__tests__/trade-validator.test.d.ts +1 -0
  108. package/dist/__tests__/trade-validator.test.js +655 -0
  109. package/dist/__tests__/utils.test.d.ts +1 -0
  110. package/dist/__tests__/utils.test.js +76 -0
  111. package/dist/api/public/hyperliquid.d.ts +18 -0
  112. package/dist/api/public/hyperliquid.js +82 -0
  113. package/dist/api/public/index.d.ts +8 -0
  114. package/dist/api/public/index.js +8 -0
  115. package/dist/api/public/lighter.d.ts +24 -0
  116. package/dist/api/public/lighter.js +100 -0
  117. package/dist/api/public/pacifica.d.ts +17 -0
  118. package/dist/api/public/pacifica.js +54 -0
  119. package/dist/api/public/urls.d.ts +12 -0
  120. package/dist/api/public/urls.js +33 -0
  121. package/dist/arb/history-stats.d.ts +44 -0
  122. package/dist/arb/history-stats.js +135 -0
  123. package/dist/arb/index.d.ts +4 -0
  124. package/dist/arb/index.js +4 -0
  125. package/dist/arb/sizing.d.ts +23 -0
  126. package/dist/arb/sizing.js +96 -0
  127. package/dist/arb/state.d.ts +51 -0
  128. package/dist/arb/state.js +112 -0
  129. package/dist/arb/utils.d.ts +81 -0
  130. package/dist/arb/utils.js +267 -0
  131. package/dist/arb-history-stats.d.ts +5 -0
  132. package/dist/arb-history-stats.js +5 -0
  133. package/dist/arb-sizing.d.ts +5 -0
  134. package/dist/arb-sizing.js +5 -0
  135. package/dist/arb-state.d.ts +5 -0
  136. package/dist/arb-state.js +5 -0
  137. package/dist/arb-utils.d.ts +5 -0
  138. package/dist/arb-utils.js +5 -0
  139. package/dist/bot/conditions.d.ts +32 -0
  140. package/dist/bot/conditions.js +141 -0
  141. package/dist/bot/config.d.ts +76 -0
  142. package/dist/bot/config.js +160 -0
  143. package/dist/bot/engine.d.ts +8 -0
  144. package/dist/bot/engine.js +519 -0
  145. package/dist/bot/presets.d.ts +11 -0
  146. package/dist/bot/presets.js +296 -0
  147. package/dist/bridge-engine.d.ts +133 -0
  148. package/dist/bridge-engine.js +1487 -0
  149. package/dist/cache.d.ts +25 -0
  150. package/dist/cache.js +99 -0
  151. package/dist/cli-spec.d.ts +50 -0
  152. package/dist/cli-spec.js +75 -0
  153. package/dist/client-id-tracker.d.ts +25 -0
  154. package/dist/client-id-tracker.js +76 -0
  155. package/dist/commands/account.d.ts +3 -0
  156. package/dist/commands/account.js +425 -0
  157. package/dist/commands/agent.d.ts +3 -0
  158. package/dist/commands/agent.js +386 -0
  159. package/dist/commands/alert.d.ts +2 -0
  160. package/dist/commands/alert.js +421 -0
  161. package/dist/commands/analytics.d.ts +3 -0
  162. package/dist/commands/analytics.js +311 -0
  163. package/dist/commands/arb/index.d.ts +3 -0
  164. package/dist/commands/arb/index.js +921 -0
  165. package/dist/commands/arb-auto.d.ts +54 -0
  166. package/dist/commands/arb-auto.js +1328 -0
  167. package/dist/commands/arb-manage.d.ts +5 -0
  168. package/dist/commands/arb-manage.js +5 -0
  169. package/dist/commands/arb.d.ts +2 -0
  170. package/dist/commands/arb.js +347 -0
  171. package/dist/commands/backtest.d.ts +2 -0
  172. package/dist/commands/backtest.js +327 -0
  173. package/dist/commands/bot.d.ts +3 -0
  174. package/dist/commands/bot.js +412 -0
  175. package/dist/commands/bridge.d.ts +2 -0
  176. package/dist/commands/bridge.js +396 -0
  177. package/dist/commands/dashboard.d.ts +3 -0
  178. package/dist/commands/dashboard.js +176 -0
  179. package/dist/commands/deposit.d.ts +4 -0
  180. package/dist/commands/deposit.js +573 -0
  181. package/dist/commands/dex.d.ts +3 -0
  182. package/dist/commands/dex.js +114 -0
  183. package/dist/commands/env.d.ts +2 -0
  184. package/dist/commands/env.js +136 -0
  185. package/dist/commands/funding.d.ts +2 -0
  186. package/dist/commands/funding.js +347 -0
  187. package/dist/commands/gap.d.ts +2 -0
  188. package/dist/commands/gap.js +305 -0
  189. package/dist/commands/health.d.ts +2 -0
  190. package/dist/commands/health.js +67 -0
  191. package/dist/commands/history.d.ts +2 -0
  192. package/dist/commands/history.js +235 -0
  193. package/dist/commands/init.d.ts +15 -0
  194. package/dist/commands/init.js +266 -0
  195. package/dist/commands/jobs.d.ts +2 -0
  196. package/dist/commands/jobs.js +133 -0
  197. package/dist/commands/manage.d.ts +4 -0
  198. package/dist/commands/manage.js +309 -0
  199. package/dist/commands/market.d.ts +3 -0
  200. package/dist/commands/market.js +225 -0
  201. package/dist/commands/plan.d.ts +3 -0
  202. package/dist/commands/plan.js +95 -0
  203. package/dist/commands/portfolio.d.ts +3 -0
  204. package/dist/commands/portfolio.js +169 -0
  205. package/dist/commands/rebalance.d.ts +3 -0
  206. package/dist/commands/rebalance.js +293 -0
  207. package/dist/commands/risk.d.ts +3 -0
  208. package/dist/commands/risk.js +169 -0
  209. package/dist/commands/run.d.ts +3 -0
  210. package/dist/commands/run.js +202 -0
  211. package/dist/commands/settings.d.ts +2 -0
  212. package/dist/commands/settings.js +102 -0
  213. package/dist/commands/stream.d.ts +5 -0
  214. package/dist/commands/stream.js +123 -0
  215. package/dist/commands/trade.d.ts +3 -0
  216. package/dist/commands/trade.js +1273 -0
  217. package/dist/commands/wallet.d.ts +14 -0
  218. package/dist/commands/wallet.js +602 -0
  219. package/dist/commands/withdraw.d.ts +3 -0
  220. package/dist/commands/withdraw.js +187 -0
  221. package/dist/config.d.ts +5 -0
  222. package/dist/config.js +68 -0
  223. package/dist/cross-chain-margin.d.ts +46 -0
  224. package/dist/cross-chain-margin.js +107 -0
  225. package/dist/dashboard/server.d.ts +80 -0
  226. package/dist/dashboard/server.js +340 -0
  227. package/dist/dashboard/ui.d.ts +4 -0
  228. package/dist/dashboard/ui.js +538 -0
  229. package/dist/dashboard/ws-feeds.d.ts +29 -0
  230. package/dist/dashboard/ws-feeds.js +660 -0
  231. package/dist/dex-asset-map.d.ts +80 -0
  232. package/dist/dex-asset-map.js +201 -0
  233. package/dist/errors.d.ts +109 -0
  234. package/dist/errors.js +84 -0
  235. package/dist/event-stream.d.ts +25 -0
  236. package/dist/event-stream.js +168 -0
  237. package/dist/exchanges/hyperliquid.d.ts +212 -0
  238. package/dist/exchanges/hyperliquid.js +931 -0
  239. package/dist/exchanges/interface.d.ts +95 -0
  240. package/dist/exchanges/interface.js +5 -0
  241. package/dist/exchanges/lighter.d.ts +159 -0
  242. package/dist/exchanges/lighter.js +793 -0
  243. package/dist/exchanges/pacifica.d.ts +51 -0
  244. package/dist/exchanges/pacifica.js +248 -0
  245. package/dist/execution-log.d.ts +36 -0
  246. package/dist/execution-log.js +102 -0
  247. package/dist/funding/history.d.ts +63 -0
  248. package/dist/funding/history.js +266 -0
  249. package/dist/funding/index.d.ts +3 -0
  250. package/dist/funding/index.js +3 -0
  251. package/dist/funding/normalize.d.ts +39 -0
  252. package/dist/funding/normalize.js +66 -0
  253. package/dist/funding/rates.d.ts +45 -0
  254. package/dist/funding/rates.js +172 -0
  255. package/dist/funding-history.d.ts +5 -0
  256. package/dist/funding-history.js +5 -0
  257. package/dist/funding-rates.d.ts +5 -0
  258. package/dist/funding-rates.js +5 -0
  259. package/dist/funding.d.ts +5 -0
  260. package/dist/funding.js +5 -0
  261. package/dist/index.d.ts +2 -0
  262. package/dist/index.js +458 -0
  263. package/dist/jobs.d.ts +37 -0
  264. package/dist/jobs.js +152 -0
  265. package/dist/liquidity.d.ts +34 -0
  266. package/dist/liquidity.js +100 -0
  267. package/dist/mcp-server.d.ts +9 -0
  268. package/dist/mcp-server.js +1206 -0
  269. package/dist/pacifica/client.d.ts +111 -0
  270. package/dist/pacifica/client.js +310 -0
  271. package/dist/pacifica/constants.d.ts +27 -0
  272. package/dist/pacifica/constants.js +47 -0
  273. package/dist/pacifica/deposit.d.ts +14 -0
  274. package/dist/pacifica/deposit.js +78 -0
  275. package/dist/pacifica/index.d.ts +6 -0
  276. package/dist/pacifica/index.js +11 -0
  277. package/dist/pacifica/signing.d.ts +49 -0
  278. package/dist/pacifica/signing.js +97 -0
  279. package/dist/pacifica/types/account.d.ts +42 -0
  280. package/dist/pacifica/types/account.js +1 -0
  281. package/dist/pacifica/types/index.d.ts +6 -0
  282. package/dist/pacifica/types/index.js +6 -0
  283. package/dist/pacifica/types/lake.d.ts +18 -0
  284. package/dist/pacifica/types/lake.js +1 -0
  285. package/dist/pacifica/types/market.d.ts +64 -0
  286. package/dist/pacifica/types/market.js +1 -0
  287. package/dist/pacifica/types/order.d.ts +92 -0
  288. package/dist/pacifica/types/order.js +1 -0
  289. package/dist/pacifica/types/position.d.ts +25 -0
  290. package/dist/pacifica/types/position.js +1 -0
  291. package/dist/pacifica/types/ws.d.ts +34 -0
  292. package/dist/pacifica/types/ws.js +41 -0
  293. package/dist/pacifica/ws-client.d.ts +42 -0
  294. package/dist/pacifica/ws-client.js +180 -0
  295. package/dist/plan-executor.d.ts +48 -0
  296. package/dist/plan-executor.js +280 -0
  297. package/dist/position-history.d.ts +68 -0
  298. package/dist/position-history.js +222 -0
  299. package/dist/rebalance.d.ts +64 -0
  300. package/dist/rebalance.js +142 -0
  301. package/dist/retry.d.ts +74 -0
  302. package/dist/retry.js +129 -0
  303. package/dist/risk.d.ts +48 -0
  304. package/dist/risk.js +156 -0
  305. package/dist/settings.d.ts +19 -0
  306. package/dist/settings.js +45 -0
  307. package/dist/shared-api.d.ts +5 -0
  308. package/dist/shared-api.js +5 -0
  309. package/dist/strategies/dca.d.ts +25 -0
  310. package/dist/strategies/dca.js +114 -0
  311. package/dist/strategies/funding-arb.d.ts +15 -0
  312. package/dist/strategies/funding-arb.js +281 -0
  313. package/dist/strategies/grid.d.ts +34 -0
  314. package/dist/strategies/grid.js +185 -0
  315. package/dist/strategies/trailing-stop.d.ts +17 -0
  316. package/dist/strategies/trailing-stop.js +121 -0
  317. package/dist/strategies/twap.d.ts +20 -0
  318. package/dist/strategies/twap.js +78 -0
  319. package/dist/trade-validator.d.ts +39 -0
  320. package/dist/trade-validator.js +154 -0
  321. package/dist/utils.d.ts +38 -0
  322. package/dist/utils.js +110 -0
  323. package/package.json +63 -0
  324. package/skills/perp-cli/SKILL.md +149 -0
  325. package/skills/perp-cli/references/commands.md +143 -0
@@ -0,0 +1,396 @@
1
+ import chalk from "chalk";
2
+ import { formatUsd, makeTable, printJson, jsonOk } from "../utils.js";
3
+ import { logExecution } from "../execution-log.js";
4
+ import { executeBestBridge, getAllQuotes, checkDebridgeStatus, checkBridgeGasBalance, CHAIN_IDS, USDC_ADDRESSES, EXCHANGE_TO_CHAIN, } from "../bridge-engine.js";
5
+ export function registerBridgeCommands(program, isJson) {
6
+ const bridge = program.command("bridge").description("Cross-chain USDC bridge (deBridge DLN)");
7
+ // ── bridge chains ──
8
+ bridge
9
+ .command("chains")
10
+ .description("List supported chains and USDC addresses")
11
+ .action(async () => {
12
+ if (isJson()) {
13
+ return printJson(jsonOk({ chains: CHAIN_IDS, usdc: USDC_ADDRESSES, exchanges: EXCHANGE_TO_CHAIN }));
14
+ }
15
+ console.log(chalk.cyan.bold("\n Supported Bridge Chains\n"));
16
+ const rows = Object.entries(CHAIN_IDS).map(([chain, id]) => [
17
+ chalk.white.bold(chain),
18
+ String(id),
19
+ USDC_ADDRESSES[chain] ? chalk.green("USDC") : chalk.gray("-"),
20
+ USDC_ADDRESSES[chain] ? chalk.gray(USDC_ADDRESSES[chain].slice(0, 10) + "...") : "",
21
+ ]);
22
+ console.log(makeTable(["Chain", "Chain ID", "Token", "Address"], rows));
23
+ console.log(chalk.cyan.bold(" Exchange → Chain Mapping\n"));
24
+ for (const [ex, chain] of Object.entries(EXCHANGE_TO_CHAIN)) {
25
+ console.log(` ${chalk.white.bold(ex.padEnd(14))} → ${chain}`);
26
+ }
27
+ console.log(chalk.gray("\n Bridge powered by deBridge DLN (~2s settlement)\n"));
28
+ });
29
+ // ── bridge quote ──
30
+ bridge
31
+ .command("quote")
32
+ .description("Get bridge quotes from all providers")
33
+ .requiredOption("--from <chain>", "Source chain (solana, arbitrum, base)")
34
+ .requiredOption("--to <chain>", "Destination chain")
35
+ .requiredOption("--amount <amount>", "USDC amount")
36
+ .option("--sender <address>", "Source address")
37
+ .option("--recipient <address>", "Destination address")
38
+ .action(async (opts) => {
39
+ const srcChain = opts.from.toLowerCase();
40
+ const dstChain = opts.to.toLowerCase();
41
+ const amount = parseFloat(opts.amount);
42
+ if (!isJson())
43
+ console.log(chalk.cyan(`\n Fetching quotes: $${amount} USDC ${srcChain} → ${dstChain}...\n`));
44
+ const quotes = await getAllQuotes(srcChain, dstChain, amount, opts.sender ?? "0x0000000000000000000000000000000000000000", opts.recipient ?? "0x0000000000000000000000000000000000000000");
45
+ if (isJson())
46
+ return printJson(jsonOk({ quotes }));
47
+ if (quotes.length === 0) {
48
+ console.log(chalk.red(" No providers available for this route.\n"));
49
+ return;
50
+ }
51
+ const PROVIDER_NAMES = {
52
+ cctp: "Circle CCTP",
53
+ relay: "Relay",
54
+ debridge: "deBridge DLN",
55
+ };
56
+ const rows = quotes.map((q, i) => {
57
+ const tag = i === 0 ? chalk.green(" ★ BEST") : "";
58
+ const name = PROVIDER_NAMES[q.provider] ?? q.provider;
59
+ const feeStr = q.fee <= 0 ? chalk.green("FREE") : `$${formatUsd(q.fee)}`;
60
+ const pct = ((q.fee / q.amountIn) * 100).toFixed(2);
61
+ const gasTag = q.gasIncluded
62
+ ? chalk.green("included")
63
+ : chalk.yellow("dst gas needed");
64
+ return [
65
+ (i === 0 ? chalk.green.bold(name) : chalk.white(name)) + tag,
66
+ `$${formatUsd(q.amountOut)}`,
67
+ `${feeStr} (${pct}%)`,
68
+ `~${q.estimatedTime}s`,
69
+ gasTag,
70
+ ];
71
+ });
72
+ console.log(chalk.cyan.bold(` Bridge Quotes: $${formatUsd(amount)} USDC ${srcChain} → ${dstChain}\n`));
73
+ console.log(makeTable(["Provider", "Receive", "Fee", "ETA", "Dst Gas"], rows));
74
+ // Show gas notes per provider
75
+ for (const q of quotes) {
76
+ if (q.gasNote) {
77
+ const name = PROVIDER_NAMES[q.provider] ?? q.provider;
78
+ console.log(chalk.gray(` ${name}: ${q.gasNote}`));
79
+ }
80
+ }
81
+ console.log();
82
+ console.log(chalk.gray(` Execute with: perp bridge send --from ${srcChain} --to ${dstChain} --amount ${opts.amount}`));
83
+ console.log(chalk.gray(` Pick provider: --provider cctp|relay|debridge\n`));
84
+ });
85
+ // ── bridge send ──
86
+ bridge
87
+ .command("send")
88
+ .description("Execute a USDC bridge transaction")
89
+ .requiredOption("--from <chain>", "Source chain")
90
+ .requiredOption("--to <chain>", "Destination chain")
91
+ .requiredOption("--amount <amount>", "USDC amount")
92
+ .option("--sender <address>", "Source address (auto-detected from key)")
93
+ .option("--recipient <address>", "Destination address")
94
+ .option("--provider <name>", "Force provider: cctp, relay, debridge (default: cheapest)")
95
+ .option("--fast", "Use fast finality (~1-2 min, $1-1.3 fee, auto-relay). Default: standard (~13-20 min, $0.01, manual relay)")
96
+ .option("--dry-run", "Show quotes without executing")
97
+ .action(async (opts) => {
98
+ const srcChain = opts.from.toLowerCase();
99
+ const dstChain = opts.to.toLowerCase();
100
+ const amount = parseFloat(opts.amount);
101
+ if (isNaN(amount) || amount <= 0)
102
+ throw new Error("Invalid amount");
103
+ const chosenProvider = opts.provider?.toLowerCase();
104
+ if (chosenProvider && !["cctp", "relay", "debridge"].includes(chosenProvider)) {
105
+ throw new Error(`Invalid provider: ${opts.provider}. Use cctp, relay, or debridge.`);
106
+ }
107
+ // Load the appropriate private key
108
+ const { loadPrivateKey, parseSolanaKeypair } = await import("../config.js");
109
+ let senderAddress;
110
+ let recipientAddress;
111
+ let signerKey;
112
+ if (srcChain === "solana") {
113
+ const pk = await loadPrivateKey("pacifica");
114
+ const keypair = parseSolanaKeypair(pk);
115
+ senderAddress = opts.sender ?? keypair.publicKey.toBase58();
116
+ signerKey = pk;
117
+ }
118
+ else {
119
+ const exchange = "hyperliquid";
120
+ const pk = await loadPrivateKey(exchange);
121
+ const { ethers } = await import("ethers");
122
+ const wallet = new ethers.Wallet(pk);
123
+ senderAddress = opts.sender ?? wallet.address;
124
+ signerKey = pk;
125
+ }
126
+ // Determine recipient address and destination signer key
127
+ let dstSignerKey;
128
+ if (opts.recipient) {
129
+ recipientAddress = opts.recipient;
130
+ }
131
+ else if (dstChain === "solana") {
132
+ const pk = await loadPrivateKey("pacifica");
133
+ const keypair = parseSolanaKeypair(pk);
134
+ recipientAddress = keypair.publicKey.toBase58();
135
+ dstSignerKey = pk;
136
+ }
137
+ else {
138
+ const exchange = "hyperliquid";
139
+ const pk = await loadPrivateKey(exchange);
140
+ const { ethers } = await import("ethers");
141
+ recipientAddress = new ethers.Wallet(pk).address;
142
+ dstSignerKey = pk;
143
+ }
144
+ // Fetch all quotes
145
+ if (!isJson())
146
+ console.log(chalk.cyan(`\n Bridge: $${amount} USDC ${srcChain} → ${dstChain}\n`));
147
+ const quotes = await getAllQuotes(srcChain, dstChain, amount, senderAddress, recipientAddress);
148
+ if (quotes.length === 0)
149
+ throw new Error(`No bridge available for ${srcChain} → ${dstChain}`);
150
+ // Pick the quote to use
151
+ let selectedQuote;
152
+ if (chosenProvider) {
153
+ const match = quotes.find(q => q.provider === chosenProvider);
154
+ if (!match)
155
+ throw new Error(`Provider "${chosenProvider}" not available for this route`);
156
+ selectedQuote = match;
157
+ }
158
+ else {
159
+ selectedQuote = quotes[0]; // best (sorted by amountOut)
160
+ }
161
+ const PROVIDER_NAMES = {
162
+ cctp: "Circle CCTP",
163
+ relay: "Relay",
164
+ debridge: "deBridge DLN",
165
+ };
166
+ if (!isJson()) {
167
+ // Show comparison table
168
+ const rows = quotes.map((q) => {
169
+ const name = PROVIDER_NAMES[q.provider] ?? q.provider;
170
+ const selected = q.provider === selectedQuote.provider;
171
+ const feeStr = q.fee <= 0 ? chalk.green("FREE") : `$${formatUsd(q.fee)}`;
172
+ const pct = ((q.fee / q.amountIn) * 100).toFixed(2);
173
+ const tag = selected ? chalk.green(" ← selected") : "";
174
+ const gasTag = q.gasIncluded
175
+ ? chalk.green("included")
176
+ : chalk.yellow("dst gas needed");
177
+ return [
178
+ (selected ? chalk.green.bold(name) : chalk.white(name)) + tag,
179
+ `$${formatUsd(q.amountOut)}`,
180
+ `${feeStr} (${pct}%)`,
181
+ `~${q.estimatedTime}s`,
182
+ gasTag,
183
+ ];
184
+ });
185
+ console.log(makeTable(["Provider", "Receive", "Fee", "ETA", "Dst Gas"], rows));
186
+ if (!selectedQuote.gasIncluded) {
187
+ console.log(chalk.yellow(` ⚠ ${selectedQuote.gasNote}`));
188
+ }
189
+ console.log(` From: ${senderAddress}`);
190
+ console.log(` To: ${recipientAddress}\n`);
191
+ }
192
+ if (opts.dryRun) {
193
+ if (isJson())
194
+ return printJson(jsonOk({ quotes, selected: selectedQuote.provider }));
195
+ console.log(chalk.yellow(" [DRY RUN] Transaction not executed.\n"));
196
+ return;
197
+ }
198
+ // Gas balance preflight check
199
+ const needsDstGas = !selectedQuote.gasIncluded;
200
+ const gasCheck = await checkBridgeGasBalance(srcChain, senderAddress, dstChain, recipientAddress, needsDstGas);
201
+ if (!gasCheck.ok) {
202
+ const msg = gasCheck.errors.map(e => ` ✗ ${e}`).join("\n");
203
+ if (isJson())
204
+ return printJson({ ok: false, error: "Insufficient gas", details: gasCheck.errors });
205
+ console.log(chalk.red("\n Insufficient gas for bridge:\n"));
206
+ console.log(chalk.red(msg));
207
+ if (needsDstGas) {
208
+ console.log(chalk.yellow("\n Tip: use --fast to skip destination gas (Circle auto-relays)\n"));
209
+ }
210
+ process.exitCode = 1;
211
+ return;
212
+ }
213
+ const providerName = PROVIDER_NAMES[selectedQuote.provider] ?? selectedQuote.provider;
214
+ if (!isJson())
215
+ console.log(chalk.yellow(` Executing via ${providerName}...\n`));
216
+ let result;
217
+ try {
218
+ result = await executeBestBridge(srcChain, dstChain, amount, signerKey, senderAddress, recipientAddress, dstSignerKey, chosenProvider, !!opts.fast);
219
+ logExecution({ type: "bridge", exchange: "bridge", symbol: "USDC", side: `${srcChain}->${dstChain}`, size: String(amount), status: "success", dryRun: false, meta: { provider: result.provider, txHash: result.txHash, fast: !!opts.fast } });
220
+ }
221
+ catch (err) {
222
+ logExecution({ type: "bridge", exchange: "bridge", symbol: "USDC", side: `${srcChain}->${dstChain}`, size: String(amount), status: "failed", dryRun: false, error: err instanceof Error ? err.message : String(err) });
223
+ throw err;
224
+ }
225
+ if (isJson())
226
+ return printJson(jsonOk(result));
227
+ console.log(chalk.green(" Bridge transaction submitted!"));
228
+ console.log(` TX Hash: ${result.txHash}`);
229
+ console.log(` Provider: ${result.provider}`);
230
+ if (result.receiveTxHash) {
231
+ console.log(` Receive: ${result.receiveTxHash} (${dstChain})`);
232
+ }
233
+ console.log(chalk.gray(`\n Funds arrive in ~${selectedQuote.estimatedTime}s on ${dstChain}.\n`));
234
+ });
235
+ // ── bridge between exchanges (shortcut) ──
236
+ bridge
237
+ .command("exchange")
238
+ .description("Bridge USDC between exchanges (shortcut)")
239
+ .requiredOption("--from <exchange>", "Source exchange (pacifica, hyperliquid, lighter)")
240
+ .requiredOption("--to <exchange>", "Destination exchange")
241
+ .requiredOption("--amount <amount>", "USDC amount")
242
+ .option("--provider <name>", "Force provider: cctp, relay, debridge (default: cheapest)")
243
+ .option("--fast", "Use fast finality (~1-2 min, $1-1.3 fee, auto-relay)")
244
+ .option("--dry-run", "Show quotes without executing")
245
+ .action(async (opts) => {
246
+ const srcExchange = opts.from.toLowerCase();
247
+ const dstExchange = opts.to.toLowerCase();
248
+ const srcChain = EXCHANGE_TO_CHAIN[srcExchange];
249
+ const dstChain = EXCHANGE_TO_CHAIN[dstExchange];
250
+ if (!srcChain)
251
+ throw new Error(`Unknown exchange: ${srcExchange}`);
252
+ if (!dstChain)
253
+ throw new Error(`Unknown exchange: ${dstExchange}`);
254
+ if (srcChain === dstChain)
255
+ throw new Error("Same chain — no bridge needed");
256
+ const chosenProvider = opts.provider?.toLowerCase();
257
+ if (chosenProvider && !["cctp", "relay", "debridge"].includes(chosenProvider)) {
258
+ throw new Error(`Invalid provider: ${opts.provider}. Use cctp, relay, or debridge.`);
259
+ }
260
+ const amount = parseFloat(opts.amount);
261
+ if (!isJson())
262
+ console.log(chalk.cyan(`\n Bridge: $${amount} USDC ${srcExchange} (${srcChain}) → ${dstExchange} (${dstChain})\n`));
263
+ // Load keys
264
+ const { loadPrivateKey, parseSolanaKeypair } = await import("../config.js");
265
+ let senderAddress;
266
+ let recipientAddress;
267
+ let signerKey;
268
+ // Source
269
+ if (srcChain === "solana") {
270
+ const pk = await loadPrivateKey("pacifica");
271
+ senderAddress = parseSolanaKeypair(pk).publicKey.toBase58();
272
+ signerKey = pk;
273
+ }
274
+ else {
275
+ const pk = await loadPrivateKey(srcExchange);
276
+ const { ethers } = await import("ethers");
277
+ senderAddress = new ethers.Wallet(pk).address;
278
+ signerKey = pk;
279
+ }
280
+ // Destination
281
+ let dstSignerKey;
282
+ if (dstChain === "solana") {
283
+ const pk = await loadPrivateKey("pacifica");
284
+ recipientAddress = parseSolanaKeypair(pk).publicKey.toBase58();
285
+ dstSignerKey = pk;
286
+ }
287
+ else {
288
+ const pk = await loadPrivateKey(dstExchange);
289
+ const { ethers } = await import("ethers");
290
+ recipientAddress = new ethers.Wallet(pk).address;
291
+ dstSignerKey = pk;
292
+ }
293
+ const quotes = await getAllQuotes(srcChain, dstChain, amount, senderAddress, recipientAddress);
294
+ if (quotes.length === 0)
295
+ throw new Error(`No bridge available for ${srcChain} → ${dstChain}`);
296
+ let selectedQuote;
297
+ if (chosenProvider) {
298
+ const match = quotes.find(q => q.provider === chosenProvider);
299
+ if (!match)
300
+ throw new Error(`Provider "${chosenProvider}" not available for this route`);
301
+ selectedQuote = match;
302
+ }
303
+ else {
304
+ selectedQuote = quotes[0];
305
+ }
306
+ const PROVIDER_NAMES = {
307
+ cctp: "Circle CCTP",
308
+ relay: "Relay",
309
+ debridge: "deBridge DLN",
310
+ };
311
+ if (!isJson()) {
312
+ const rows = quotes.map((q) => {
313
+ const name = PROVIDER_NAMES[q.provider] ?? q.provider;
314
+ const selected = q.provider === selectedQuote.provider;
315
+ const feeStr = q.fee <= 0 ? chalk.green("FREE") : `$${formatUsd(q.fee)}`;
316
+ const pct = ((q.fee / q.amountIn) * 100).toFixed(2);
317
+ const tag = selected ? chalk.green(" ← selected") : "";
318
+ const gasTag = q.gasIncluded
319
+ ? chalk.green("included")
320
+ : chalk.yellow("dst gas needed");
321
+ return [
322
+ (selected ? chalk.green.bold(name) : chalk.white(name)) + tag,
323
+ `$${formatUsd(q.amountOut)}`,
324
+ `${feeStr} (${pct}%)`,
325
+ `~${q.estimatedTime}s`,
326
+ gasTag,
327
+ ];
328
+ });
329
+ console.log(makeTable(["Provider", "Receive", "Fee", "ETA", "Dst Gas"], rows));
330
+ if (!selectedQuote.gasIncluded) {
331
+ console.log(chalk.yellow(` ⚠ ${selectedQuote.gasNote}`));
332
+ }
333
+ }
334
+ if (opts.dryRun) {
335
+ if (isJson())
336
+ return printJson(jsonOk({ quotes, selected: selectedQuote.provider }));
337
+ console.log(chalk.yellow(" [DRY RUN] Not executed.\n"));
338
+ return;
339
+ }
340
+ // Gas balance preflight check
341
+ const needsDstGas = !selectedQuote.gasIncluded;
342
+ const gasCheck = await checkBridgeGasBalance(srcChain, senderAddress, dstChain, recipientAddress, needsDstGas);
343
+ if (!gasCheck.ok) {
344
+ const msg = gasCheck.errors.map(e => ` ✗ ${e}`).join("\n");
345
+ if (isJson())
346
+ return printJson({ ok: false, error: "Insufficient gas", details: gasCheck.errors });
347
+ console.log(chalk.red("\n Insufficient gas for bridge:\n"));
348
+ console.log(chalk.red(msg));
349
+ if (needsDstGas) {
350
+ console.log(chalk.yellow("\n Tip: use --fast to skip destination gas (Circle auto-relays)\n"));
351
+ }
352
+ process.exitCode = 1;
353
+ return;
354
+ }
355
+ const providerName = PROVIDER_NAMES[selectedQuote.provider] ?? selectedQuote.provider;
356
+ if (!isJson())
357
+ console.log(chalk.yellow(` Executing via ${providerName}...\n`));
358
+ let result;
359
+ try {
360
+ result = await executeBestBridge(srcChain, dstChain, amount, signerKey, senderAddress, recipientAddress, dstSignerKey, chosenProvider, !!opts.fast);
361
+ logExecution({ type: "bridge", exchange: "bridge", symbol: "USDC", side: `${srcExchange}->${dstExchange}`, size: String(amount), status: "success", dryRun: false, meta: { provider: result.provider, txHash: result.txHash, fast: !!opts.fast } });
362
+ }
363
+ catch (err) {
364
+ logExecution({ type: "bridge", exchange: "bridge", symbol: "USDC", side: `${srcExchange}->${dstExchange}`, size: String(amount), status: "failed", dryRun: false, error: err instanceof Error ? err.message : String(err) });
365
+ throw err;
366
+ }
367
+ if (isJson())
368
+ return printJson(jsonOk(result));
369
+ console.log(chalk.green(" Bridge submitted!"));
370
+ console.log(` TX: ${result.txHash}`);
371
+ console.log(` Provider: ${result.provider}`);
372
+ if (result.receiveTxHash) {
373
+ console.log(` Receive: ${result.receiveTxHash} (${dstChain})`);
374
+ console.log(chalk.green(` USDC arrived on ${dstExchange}!`));
375
+ }
376
+ console.log(chalk.gray(`\n After arrival, deposit into ${dstExchange}:`));
377
+ console.log(chalk.gray(` perp deposit ${dstExchange} ${opts.amount}\n`));
378
+ });
379
+ // ── bridge status ──
380
+ bridge
381
+ .command("status <orderId>")
382
+ .description("Check bridge order status")
383
+ .action(async (orderId) => {
384
+ const status = await checkDebridgeStatus(orderId);
385
+ if (isJson())
386
+ return printJson(jsonOk(status));
387
+ const state = String(status.status ?? status.orderStatus ?? "unknown");
388
+ const stateColor = state.includes("Fulfilled") || state.includes("completed")
389
+ ? chalk.green
390
+ : state.includes("pending") ? chalk.yellow : chalk.white;
391
+ console.log(chalk.cyan.bold("\n Bridge Order Status\n"));
392
+ console.log(` Order ID: ${orderId}`);
393
+ console.log(` Status: ${stateColor(state)}`);
394
+ console.log();
395
+ });
396
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ import type { ExchangeAdapter } from "../exchanges/interface.js";
3
+ export declare function registerDashboardCommands(program: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean, getHLAdapterForDex?: (dex: string) => Promise<ExchangeAdapter>): void;
@@ -0,0 +1,176 @@
1
+ import chalk from "chalk";
2
+ import { startDashboard } from "../dashboard/server.js";
3
+ const KNOWN_HIP3_DEXES = ["xyz", "flx", "hyna", "km", "cash", "vntl"];
4
+ const HL_INFO_URL = "https://api.hyperliquid.xyz/info";
5
+ /** Lightweight check: query HL info API directly for dex positions (no SDK/WebSocket needed) */
6
+ async function checkDexPositions(address, dex) {
7
+ try {
8
+ const res = await fetch(HL_INFO_URL, {
9
+ method: "POST",
10
+ headers: { "Content-Type": "application/json" },
11
+ body: JSON.stringify({ type: "clearinghouseState", user: address, dex }),
12
+ });
13
+ const state = await res.json();
14
+ const positions = (state?.assetPositions ?? []);
15
+ return positions.filter(p => {
16
+ const pos = (p.position ?? p);
17
+ return Number(pos.szi ?? 0) !== 0;
18
+ }).length;
19
+ }
20
+ catch {
21
+ return 0;
22
+ }
23
+ }
24
+ /** Scan known HIP-3 dexes for active positions, then create adapters only for active ones */
25
+ async function autoDetectHip3Dexes(address, getHLAdapterForDex, verbose) {
26
+ // Phase 1: lightweight parallel scan via raw API (no WebSocket connections)
27
+ const checks = await Promise.all(KNOWN_HIP3_DEXES.map(async (dex) => ({
28
+ dex,
29
+ count: await checkDexPositions(address, dex),
30
+ })));
31
+ const activeDexes = checks.filter(c => c.count > 0);
32
+ // Phase 2: create full adapters only for dexes with positions
33
+ const results = [];
34
+ for (const { dex, count } of activeDexes) {
35
+ try {
36
+ const adapter = await getHLAdapterForDex(dex);
37
+ results.push({ name: `hl:${dex}`, adapter });
38
+ if (verbose)
39
+ console.log(chalk.green(` ✓ hl:${dex} (HIP-3, ${count} position${count > 1 ? "s" : ""})`));
40
+ }
41
+ catch {
42
+ // skip
43
+ }
44
+ }
45
+ return results;
46
+ }
47
+ export function registerDashboardCommands(program, getAdapterForExchange, isJson, getHLAdapterForDex) {
48
+ program
49
+ .command("dashboard")
50
+ .description("Launch live web dashboard for real-time portfolio monitoring")
51
+ .option("-p, --port <port>", "Port to serve dashboard on (auto-finds free port)", "3456")
52
+ .option("--interval <ms>", "Poll interval in milliseconds", "5000")
53
+ .option("--exchanges <list>", "Comma-separated exchanges to monitor", "pacifica,hyperliquid,lighter")
54
+ .option("--dex <list>", "Comma-separated HIP-3 dexes to monitor (e.g. xyz,flx,hyna)")
55
+ .option("--no-auto-dex", "Disable auto-detection of HIP-3 dexes with active positions")
56
+ .action(async (opts) => {
57
+ const exchangeNames = opts.exchanges.split(",").map((s) => s.trim()).filter(Boolean);
58
+ const dexNames = opts.dex ? opts.dex.split(",").map((s) => s.trim()).filter(Boolean) : [];
59
+ const autoDex = opts.autoDex !== false;
60
+ const port = parseInt(opts.port, 10);
61
+ const interval = parseInt(opts.interval, 10);
62
+ if (isJson()) {
63
+ const { printJson, jsonOk } = await import("../utils.js");
64
+ const exchanges = [];
65
+ for (const name of exchangeNames) {
66
+ try {
67
+ const adapter = await getAdapterForExchange(name);
68
+ exchanges.push({ name, adapter });
69
+ }
70
+ catch { /* skip */ }
71
+ }
72
+ if (getHLAdapterForDex) {
73
+ for (const dex of dexNames) {
74
+ try {
75
+ const adapter = await getHLAdapterForDex(dex);
76
+ exchanges.push({ name: `hl:${dex}`, adapter });
77
+ }
78
+ catch { /* skip */ }
79
+ }
80
+ if (autoDex && !dexNames.length && exchangeNames.includes("hyperliquid")) {
81
+ const hlEx = exchanges.find(e => e.name === "hyperliquid");
82
+ const hlAddr = hlEx ? hlEx.adapter.address ?? "" : "";
83
+ if (hlAddr) {
84
+ const detected = await autoDetectHip3Dexes(hlAddr, getHLAdapterForDex, false);
85
+ exchanges.push(...detected);
86
+ }
87
+ }
88
+ }
89
+ if (!exchanges.length) {
90
+ const { jsonError } = await import("../utils.js");
91
+ console.log(JSON.stringify(jsonError("NO_EXCHANGES", "No exchange adapters could be initialized. Check your keys.")));
92
+ process.exit(1);
93
+ }
94
+ const dashboard = await startDashboard(exchanges, { port, pollInterval: interval });
95
+ printJson(jsonOk({ url: `http://localhost:${dashboard.port}`, port: dashboard.port, exchanges: exchanges.map((e) => e.name) }));
96
+ await new Promise(() => { });
97
+ return;
98
+ }
99
+ console.log(chalk.cyan.bold("\n perp-cli Live Dashboard\n"));
100
+ console.log(chalk.gray(` Initializing exchanges: ${exchangeNames.join(", ")}${dexNames.length ? ` + HIP-3: ${dexNames.join(", ")}` : ""}...\n`));
101
+ const exchanges = [];
102
+ for (const name of exchangeNames) {
103
+ try {
104
+ const adapter = await getAdapterForExchange(name);
105
+ exchanges.push({ name, adapter });
106
+ console.log(chalk.green(` ✓ ${name}`));
107
+ }
108
+ catch (err) {
109
+ const msg = err instanceof Error ? err.message : String(err);
110
+ console.log(chalk.yellow(` ✗ ${name}: ${msg.slice(0, 80)}`));
111
+ }
112
+ }
113
+ // Initialize HIP-3 dex adapters (explicit --dex)
114
+ if (getHLAdapterForDex) {
115
+ for (const dex of dexNames) {
116
+ try {
117
+ const adapter = await getHLAdapterForDex(dex);
118
+ exchanges.push({ name: `hl:${dex}`, adapter });
119
+ console.log(chalk.green(` ✓ hl:${dex} (HIP-3)`));
120
+ }
121
+ catch (err) {
122
+ const msg = err instanceof Error ? err.message : String(err);
123
+ console.log(chalk.yellow(` ✗ hl:${dex}: ${msg.slice(0, 80)}`));
124
+ }
125
+ }
126
+ // Auto-detect HIP-3 dexes with active positions (when --dex not specified)
127
+ if (autoDex && !dexNames.length && exchangeNames.includes("hyperliquid")) {
128
+ const hlEx = exchanges.find(e => e.name === "hyperliquid");
129
+ const hlAddr = hlEx ? hlEx.adapter.address ?? "" : "";
130
+ if (hlAddr) {
131
+ console.log(chalk.gray(" Scanning HIP-3 dexes for active positions..."));
132
+ const detected = await autoDetectHip3Dexes(hlAddr, getHLAdapterForDex, true);
133
+ exchanges.push(...detected);
134
+ if (!detected.length)
135
+ console.log(chalk.gray(" No active HIP-3 dex positions found"));
136
+ }
137
+ }
138
+ }
139
+ if (!exchanges.length) {
140
+ console.error(chalk.red("\n No exchanges available. Check your private keys in .env\n"));
141
+ process.exit(1);
142
+ }
143
+ console.log(chalk.gray(`\n Starting server on port ${port}...`));
144
+ const dashboard = await startDashboard(exchanges, { port, pollInterval: interval });
145
+ console.log(chalk.cyan.bold(`\n Dashboard running at: `) + chalk.white.bold(`http://localhost:${dashboard.port}`));
146
+ console.log(chalk.gray(` Monitoring: ${exchanges.map((e) => e.name).join(", ")}`));
147
+ console.log(chalk.gray(` Poll interval: ${interval}ms`));
148
+ console.log(chalk.gray(` Press Ctrl+C to stop\n`));
149
+ // Open browser automatically (best-effort)
150
+ try {
151
+ const { exec } = await import("child_process");
152
+ const url = `http://localhost:${dashboard.port}`;
153
+ if (process.platform === "darwin")
154
+ exec(`open ${url}`);
155
+ else if (process.platform === "linux")
156
+ exec(`xdg-open ${url}`);
157
+ }
158
+ catch {
159
+ // ignore — user can open manually
160
+ }
161
+ // Keep process running
162
+ const ac = new AbortController();
163
+ process.on("SIGINT", () => {
164
+ console.log(chalk.gray("\n Shutting down dashboard..."));
165
+ dashboard.close();
166
+ ac.abort();
167
+ process.exit(0);
168
+ });
169
+ process.on("SIGTERM", () => {
170
+ dashboard.close();
171
+ ac.abort();
172
+ process.exit(0);
173
+ });
174
+ await new Promise(() => { });
175
+ });
176
+ }
@@ -0,0 +1,4 @@
1
+ import { Command } from "commander";
2
+ import type { ExchangeAdapter } from "../exchanges/interface.js";
3
+ import type { Network } from "../pacifica/index.js";
4
+ export declare function registerDepositCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getNetwork: () => Network): void;