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,187 @@
1
+ import chalk from "chalk";
2
+ import { printJson, formatUsd, jsonOk } from "../utils.js";
3
+ import { PacificaAdapter } from "../exchanges/pacifica.js";
4
+ import { HyperliquidAdapter } from "../exchanges/hyperliquid.js";
5
+ import { logExecution } from "../execution-log.js";
6
+ export function registerWithdrawCommands(program, getAdapter, isJson) {
7
+ const withdraw = program.command("withdraw").description("Withdraw funds from exchange accounts");
8
+ // ── Pacifica ──
9
+ withdraw
10
+ .command("pacifica <amount>")
11
+ .description("Withdraw USDC from Pacifica to your Solana wallet")
12
+ .option("--to <address>", "Destination Solana address (default: your wallet)")
13
+ .action(async (amount, opts) => {
14
+ const amountNum = parseFloat(amount);
15
+ if (isNaN(amountNum) || amountNum <= 0)
16
+ throw new Error("Invalid amount");
17
+ const adapter = await getAdapter();
18
+ if (!(adapter instanceof PacificaAdapter))
19
+ throw new Error("Requires --exchange pacifica");
20
+ const dest = opts.to || adapter.publicKey;
21
+ if (!isJson()) {
22
+ console.log(chalk.cyan(`\n Withdrawing $${formatUsd(amountNum)} USDC from Pacifica...\n`));
23
+ console.log(` Destination: ${dest}`);
24
+ }
25
+ try {
26
+ const result = await adapter.sdk.withdraw({ amount: String(amountNum), dest_address: dest }, adapter.publicKey, adapter.signer);
27
+ logExecution({
28
+ type: "bridge", exchange: "pacifica", symbol: "USDC", side: "withdraw",
29
+ size: String(amountNum), status: "success", dryRun: false,
30
+ meta: { action: "withdraw", destination: dest },
31
+ });
32
+ if (isJson())
33
+ return printJson(jsonOk(result));
34
+ console.log(chalk.green(`\n Withdrawal submitted!`));
35
+ console.log(` Amount: $${formatUsd(amountNum)} USDC`);
36
+ console.log(chalk.gray(`\n Funds arrive in your Solana wallet shortly.\n`));
37
+ }
38
+ catch (err) {
39
+ logExecution({
40
+ type: "bridge", exchange: "pacifica", symbol: "USDC", side: "withdraw",
41
+ size: String(amountNum), status: "failed", dryRun: false,
42
+ error: err instanceof Error ? err.message : String(err),
43
+ meta: { action: "withdraw", destination: dest },
44
+ });
45
+ throw err;
46
+ }
47
+ });
48
+ // ── Hyperliquid ──
49
+ withdraw
50
+ .command("hyperliquid <amount>")
51
+ .description("Withdraw USDC from Hyperliquid to your Arbitrum wallet")
52
+ .option("--to <address>", "Destination EVM address (default: your wallet)")
53
+ .action(async (amount, opts) => {
54
+ const amountNum = parseFloat(amount);
55
+ if (isNaN(amountNum) || amountNum <= 0)
56
+ throw new Error("Invalid amount");
57
+ const adapter = await getAdapter();
58
+ if (!(adapter instanceof HyperliquidAdapter))
59
+ throw new Error("Requires --exchange hyperliquid");
60
+ const dest = opts.to || adapter.address;
61
+ if (!isJson()) {
62
+ console.log(chalk.cyan(`\n Withdrawing $${formatUsd(amountNum)} USDC from Hyperliquid...\n`));
63
+ console.log(` Destination: ${dest}`);
64
+ }
65
+ // Check withdrawable balance
66
+ const balance = await adapter.getBalance();
67
+ const available = Number(balance.available);
68
+ if (amountNum > available) {
69
+ console.error(chalk.red(` Insufficient withdrawable balance. Available: $${formatUsd(available)}`));
70
+ process.exit(1);
71
+ }
72
+ try {
73
+ const result = await adapter.withdraw(String(amountNum), dest);
74
+ logExecution({
75
+ type: "bridge", exchange: "hyperliquid", symbol: "USDC", side: "withdraw",
76
+ size: String(amountNum), status: "success", dryRun: false,
77
+ meta: { action: "withdraw", destination: dest },
78
+ });
79
+ if (isJson())
80
+ return printJson(jsonOk(result));
81
+ console.log(chalk.green(`\n Withdrawal submitted!`));
82
+ console.log(` Amount: $${formatUsd(amountNum)} USDC`);
83
+ console.log(chalk.gray(`\n Processing may take a few minutes.\n`));
84
+ }
85
+ catch (err) {
86
+ logExecution({
87
+ type: "bridge", exchange: "hyperliquid", symbol: "USDC", side: "withdraw",
88
+ size: String(amountNum), status: "failed", dryRun: false,
89
+ error: err instanceof Error ? err.message : String(err),
90
+ meta: { action: "withdraw", destination: dest },
91
+ });
92
+ throw err;
93
+ }
94
+ });
95
+ // ── Lighter ──
96
+ withdraw
97
+ .command("lighter <amount>")
98
+ .description("Withdraw USDC from Lighter to your Ethereum L1 wallet")
99
+ .option("--asset-id <id>", "Asset ID (default: 2 = USDC)", "2")
100
+ .option("--route <type>", "Route type: 0=perp, 1=spot (default: 0)", "0")
101
+ .action(async (amount, opts) => {
102
+ const amountNum = parseFloat(amount);
103
+ if (isNaN(amountNum) || amountNum <= 0)
104
+ throw new Error("Invalid amount");
105
+ const adapter = await getAdapter();
106
+ // Lazy-import to check instance
107
+ const { LighterAdapter } = await import("../exchanges/lighter.js");
108
+ if (!(adapter instanceof LighterAdapter))
109
+ throw new Error("Requires --exchange lighter");
110
+ if (!isJson()) {
111
+ console.log(chalk.cyan(`\n Withdrawing $${formatUsd(amountNum)} USDC from Lighter...\n`));
112
+ console.log(` Account Index: ${adapter.accountIndex}`);
113
+ console.log(` Address: ${adapter.address}`);
114
+ }
115
+ try {
116
+ const result = await adapter.withdraw(amountNum, parseInt(opts.assetId), parseInt(opts.route));
117
+ logExecution({
118
+ type: "bridge", exchange: "lighter", symbol: "USDC", side: "withdraw",
119
+ size: String(amountNum), status: "success", dryRun: false,
120
+ meta: { action: "withdraw" },
121
+ });
122
+ if (isJson())
123
+ return printJson(jsonOk(result));
124
+ console.log(chalk.green(`\n Withdrawal submitted!`));
125
+ console.log(` Amount: $${formatUsd(amountNum)} USDC`);
126
+ console.log(chalk.gray(`\n Standard withdrawal takes ~12 hours. Use Lighter web for fast withdrawal.\n`));
127
+ }
128
+ catch (err) {
129
+ logExecution({
130
+ type: "bridge", exchange: "lighter", symbol: "USDC", side: "withdraw",
131
+ size: String(amountNum), status: "failed", dryRun: false,
132
+ error: err instanceof Error ? err.message : String(err),
133
+ meta: { action: "withdraw" },
134
+ });
135
+ throw err;
136
+ }
137
+ });
138
+ // ── Transfer (HL internal) ──
139
+ withdraw
140
+ .command("transfer <amount> <destination>")
141
+ .description("Transfer USDC between Hyperliquid accounts (internal, instant)")
142
+ .action(async (amount, destination) => {
143
+ const amountNum = parseFloat(amount);
144
+ if (isNaN(amountNum) || amountNum <= 0)
145
+ throw new Error("Invalid amount");
146
+ const adapter = await getAdapter();
147
+ if (!(adapter instanceof HyperliquidAdapter))
148
+ throw new Error("Requires --exchange hyperliquid");
149
+ if (!isJson())
150
+ console.log(chalk.cyan(`\n Transferring $${formatUsd(amountNum)} USDC to ${destination}...\n`));
151
+ const result = await adapter.usdTransfer(amountNum, destination);
152
+ if (isJson())
153
+ return printJson(jsonOk(result));
154
+ console.log(chalk.green(` Transfer complete!`));
155
+ console.log(` Amount: $${formatUsd(amountNum)} USDC`);
156
+ console.log(` To: ${destination}\n`);
157
+ });
158
+ // ── Info ──
159
+ withdraw
160
+ .command("info")
161
+ .description("Show withdrawal info & limits")
162
+ .action(async () => {
163
+ if (isJson()) {
164
+ return printJson(jsonOk({
165
+ exchanges: {
166
+ pacifica: { chain: "Solana", token: "USDC", speed: "~10s", fee: "none" },
167
+ hyperliquid: { chain: "Arbitrum", token: "USDC", speed: "minutes", fee: "$1" },
168
+ lighter: { chain: "Ethereum L1", token: "USDC", speed: "~12 hours (standard), minutes (fast)", fee: "varies" },
169
+ },
170
+ }));
171
+ }
172
+ console.log(chalk.cyan.bold("\n Withdrawal Info\n"));
173
+ console.log(chalk.white.bold(" Pacifica") + chalk.gray(" → Solana"));
174
+ console.log(` Speed: ~10s | Fee: none`);
175
+ console.log(` Command: ${chalk.green("perp withdraw pacifica <amount>")}`);
176
+ console.log(chalk.white.bold("\n Hyperliquid") + chalk.gray(" → Arbitrum"));
177
+ console.log(` Speed: minutes | Fee: ~$1`);
178
+ console.log(` Command: ${chalk.green("perp -e hyperliquid withdraw hyperliquid <amount>")}`);
179
+ console.log(chalk.white.bold("\n Lighter") + chalk.gray(" → Ethereum L1"));
180
+ console.log(` Speed: ~12h (standard) | Fee: varies`);
181
+ console.log(` Command: ${chalk.green("perp -e lighter withdraw lighter <amount>")}`);
182
+ console.log(chalk.white.bold("\n Internal Transfer") + chalk.gray(" (Hyperliquid → Hyperliquid)"));
183
+ console.log(` Speed: instant | Fee: none`);
184
+ console.log(` Command: ${chalk.green("perp -e hyperliquid withdraw transfer <amount> <address>")}`);
185
+ console.log();
186
+ });
187
+ }
@@ -0,0 +1,5 @@
1
+ import { Keypair } from "@solana/web3.js";
2
+ export type Exchange = "pacifica" | "hyperliquid" | "lighter";
3
+ export declare function loadPrivateKey(exchange: Exchange, pkOverride?: string): Promise<string>;
4
+ export declare function parseSolanaKeypair(input: string): Keypair;
5
+ export declare function isEvmPrivateKey(input: string): boolean;
package/dist/config.js ADDED
@@ -0,0 +1,68 @@
1
+ import { Keypair } from "@solana/web3.js";
2
+ import bs58 from "bs58";
3
+ import chalk from "chalk";
4
+ import { resolve } from "path";
5
+ import { existsSync, readFileSync } from "fs";
6
+ export async function loadPrivateKey(exchange, pkOverride) {
7
+ // 1. CLI flag
8
+ if (pkOverride)
9
+ return pkOverride;
10
+ // 2. Exchange-specific env vars
11
+ const envMap = {
12
+ pacifica: ["PACIFICA_PRIVATE_KEY", "pk"],
13
+ hyperliquid: ["HYPERLIQUID_PRIVATE_KEY", "HL_PRIVATE_KEY"],
14
+ lighter: ["LIGHTER_PRIVATE_KEY"],
15
+ };
16
+ for (const envVar of envMap[exchange]) {
17
+ if (process.env[envVar])
18
+ return process.env[envVar];
19
+ }
20
+ // 3. Generic fallback
21
+ if (process.env.PRIVATE_KEY)
22
+ return process.env.PRIVATE_KEY;
23
+ // 4. Active wallet from wallets.json
24
+ try {
25
+ const { getActiveWalletKey } = await import("./commands/wallet.js");
26
+ const walletKey = getActiveWalletKey(exchange);
27
+ if (walletKey)
28
+ return walletKey;
29
+ }
30
+ catch {
31
+ // wallet module not available, skip
32
+ }
33
+ // 5. Legacy key file (~/.perp/<exchange>.key)
34
+ const keyFile = resolve(process.env.HOME || "~", ".perp", `${exchange}.key`);
35
+ if (existsSync(keyFile)) {
36
+ return readFileSync(keyFile, "utf-8").trim();
37
+ }
38
+ throw new Error(`No private key configured for ${exchange}.\n\n` +
39
+ ` Quick start: ${chalk.cyan("perp init")}\n\n` +
40
+ ` Or manually:\n` +
41
+ ` perp wallet set ${exchange} <key>\n` +
42
+ ` ${envMap[exchange][0]}=<key> (env var)\n` +
43
+ ` --private-key <key> (per-command)`);
44
+ }
45
+ export function parseSolanaKeypair(input) {
46
+ // Try base58 first
47
+ try {
48
+ const bytes = bs58.decode(input);
49
+ return Keypair.fromSecretKey(bytes);
50
+ }
51
+ catch {
52
+ // noop
53
+ }
54
+ // Try JSON array (Solana CLI format)
55
+ try {
56
+ const arr = JSON.parse(input);
57
+ if (Array.isArray(arr)) {
58
+ return Keypair.fromSecretKey(Uint8Array.from(arr));
59
+ }
60
+ }
61
+ catch {
62
+ // noop
63
+ }
64
+ throw new Error("Invalid Solana private key. Expected base58 or JSON byte array.");
65
+ }
66
+ export function isEvmPrivateKey(input) {
67
+ return input.startsWith("0x") && input.length === 66;
68
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Cross-chain margin monitoring and dynamic auto-sizing.
3
+ *
4
+ * Provides:
5
+ * - Per-exchange margin status checking
6
+ * - Auto-size computation based on orderbook depth and margin limits
7
+ */
8
+ import type { ExchangeAdapter } from "./exchanges/interface.js";
9
+ export interface ChainMarginStatus {
10
+ exchange: string;
11
+ chain: string;
12
+ equity: number;
13
+ usedMargin: number;
14
+ freeMargin: number;
15
+ marginRatio: number;
16
+ belowThreshold: boolean;
17
+ }
18
+ /**
19
+ * Check margin status across all configured exchanges.
20
+ *
21
+ * @param adapters - Map of exchange name to adapter
22
+ * @param minMarginPct - Warn when free margin / equity falls below this (default: 30%)
23
+ * @returns Per-exchange margin status array
24
+ */
25
+ export declare function checkChainMargins(adapters: Map<string, ExchangeAdapter>, minMarginPct?: number): Promise<ChainMarginStatus[]>;
26
+ /**
27
+ * Check if a specific exchange has critically low margin (below 15%).
28
+ */
29
+ export declare function isCriticalMargin(status: ChainMarginStatus): boolean;
30
+ /**
31
+ * Check if a specific exchange should block new entries (below threshold).
32
+ */
33
+ export declare function shouldBlockEntries(status: ChainMarginStatus, minMarginPct?: number): boolean;
34
+ /**
35
+ * Compute the optimal position size automatically based on:
36
+ * 1. Orderbook depth on both sides
37
+ * 2. Free margin constraints (max 50% per trade)
38
+ * 3. Risk config maxPositionUsd
39
+ *
40
+ * @param longAdapter - Adapter for the long side exchange
41
+ * @param shortAdapter - Adapter for the short side exchange
42
+ * @param symbol - Trading symbol
43
+ * @param maxSlippagePct - Max acceptable slippage (default: 0.3%)
44
+ * @returns USD size per leg, or 0 if not viable
45
+ */
46
+ export declare function computeAutoSize(longAdapter: ExchangeAdapter, shortAdapter: ExchangeAdapter, symbol: string, maxSlippagePct?: number): Promise<number>;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Cross-chain margin monitoring and dynamic auto-sizing.
3
+ *
4
+ * Provides:
5
+ * - Per-exchange margin status checking
6
+ * - Auto-size computation based on orderbook depth and margin limits
7
+ */
8
+ import { computeExecutableSize } from "./liquidity.js";
9
+ import { loadRiskLimits } from "./risk.js";
10
+ const EXCHANGE_CHAIN_MAP = {
11
+ lighter: "arbitrum",
12
+ pacifica: "solana",
13
+ hyperliquid: "hyperliquid",
14
+ };
15
+ /**
16
+ * Check margin status across all configured exchanges.
17
+ *
18
+ * @param adapters - Map of exchange name to adapter
19
+ * @param minMarginPct - Warn when free margin / equity falls below this (default: 30%)
20
+ * @returns Per-exchange margin status array
21
+ */
22
+ export async function checkChainMargins(adapters, minMarginPct = 30) {
23
+ const entries = [...adapters.entries()];
24
+ const results = await Promise.allSettled(entries.map(async ([name, adapter]) => {
25
+ const bal = await adapter.getBalance();
26
+ const equity = Number(bal.equity);
27
+ const usedMargin = Number(bal.marginUsed);
28
+ const freeMargin = equity - usedMargin;
29
+ // marginRatio = (freeMargin / equity) * 100
30
+ // If equity is 0, ratio is 0 (critical). If no margin used, ratio is 100.
31
+ const marginRatio = equity > 0 ? (freeMargin / equity) * 100 : 0;
32
+ return {
33
+ exchange: name,
34
+ chain: EXCHANGE_CHAIN_MAP[name.toLowerCase()] ?? "unknown",
35
+ equity,
36
+ usedMargin,
37
+ freeMargin,
38
+ marginRatio,
39
+ belowThreshold: marginRatio < minMarginPct,
40
+ };
41
+ }));
42
+ return results
43
+ .filter((r) => r.status === "fulfilled")
44
+ .map((r) => r.value);
45
+ }
46
+ /**
47
+ * Check if a specific exchange has critically low margin (below 15%).
48
+ */
49
+ export function isCriticalMargin(status) {
50
+ return status.marginRatio < 15;
51
+ }
52
+ /**
53
+ * Check if a specific exchange should block new entries (below threshold).
54
+ */
55
+ export function shouldBlockEntries(status, minMarginPct = 30) {
56
+ return status.marginRatio < minMarginPct;
57
+ }
58
+ // ── Dynamic Auto-Sizing ──
59
+ /**
60
+ * Compute the optimal position size automatically based on:
61
+ * 1. Orderbook depth on both sides
62
+ * 2. Free margin constraints (max 50% per trade)
63
+ * 3. Risk config maxPositionUsd
64
+ *
65
+ * @param longAdapter - Adapter for the long side exchange
66
+ * @param shortAdapter - Adapter for the short side exchange
67
+ * @param symbol - Trading symbol
68
+ * @param maxSlippagePct - Max acceptable slippage (default: 0.3%)
69
+ * @returns USD size per leg, or 0 if not viable
70
+ */
71
+ export async function computeAutoSize(longAdapter, shortAdapter, symbol, maxSlippagePct = 0.3) {
72
+ // Step 1: Get orderbooks from both sides
73
+ const [longOB, shortOB, longBal, shortBal] = await Promise.all([
74
+ longAdapter.getOrderbook(symbol),
75
+ shortAdapter.getOrderbook(symbol),
76
+ longAdapter.getBalance(),
77
+ shortAdapter.getBalance(),
78
+ ]);
79
+ // Step 2: Compute executable size on each side
80
+ // For long entry we consume asks, for short entry we consume bids
81
+ // Use a large requested size to discover the full depth available
82
+ const probeSize = 1_000_000; // $1M probe to find max depth
83
+ const longCheck = computeExecutableSize(longOB.asks, probeSize, maxSlippagePct);
84
+ const shortCheck = computeExecutableSize(shortOB.bids, probeSize, maxSlippagePct);
85
+ // Step 3: Take the minimum of both sides (in USD)
86
+ const longMaxUsd = longCheck.maxSize * longCheck.avgFillPrice;
87
+ const shortMaxUsd = shortCheck.maxSize * shortCheck.avgFillPrice;
88
+ let sizeUsd = Math.min(longMaxUsd, shortMaxUsd);
89
+ if (sizeUsd <= 0)
90
+ return 0;
91
+ // Step 4: Cap at 50% of free margin on either exchange
92
+ const longEquity = Number(longBal.equity);
93
+ const longUsed = Number(longBal.marginUsed);
94
+ const longFree = longEquity - longUsed;
95
+ const shortEquity = Number(shortBal.equity);
96
+ const shortUsed = Number(shortBal.marginUsed);
97
+ const shortFree = shortEquity - shortUsed;
98
+ const maxByMargin = Math.min(longFree, shortFree) * 0.5;
99
+ if (maxByMargin > 0) {
100
+ sizeUsd = Math.min(sizeUsd, maxByMargin);
101
+ }
102
+ // Step 5: Cap at maxPositionUsd from risk config
103
+ const riskLimits = loadRiskLimits();
104
+ sizeUsd = Math.min(sizeUsd, riskLimits.maxPositionUsd);
105
+ // Round to nearest dollar
106
+ return Math.floor(sizeUsd);
107
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Live Dashboard Server — HTTP + WebSocket for real-time portfolio monitoring.
3
+ *
4
+ * Polls all configured exchange adapters and pushes updates to connected clients.
5
+ * Includes cross-exchange arb data: funding rate comparison + dex arb scan.
6
+ */
7
+ import type { ExchangeAdapter, ExchangeBalance, ExchangePosition, ExchangeOrder, ExchangeMarketInfo } from "../exchanges/interface.js";
8
+ export interface DashboardExchange {
9
+ name: string;
10
+ adapter: ExchangeAdapter;
11
+ }
12
+ export interface ArbOpportunity {
13
+ symbol: string;
14
+ longExchange: string;
15
+ shortExchange: string;
16
+ spreadAnnual: number;
17
+ estHourlyUsd: number;
18
+ rates: {
19
+ exchange: string;
20
+ annualizedPct: number;
21
+ hourlyRate: number;
22
+ markPrice: number;
23
+ }[];
24
+ }
25
+ export interface DexArbOpportunity {
26
+ underlying: string;
27
+ longDex: string;
28
+ shortDex: string;
29
+ annualSpread: number;
30
+ priceGapPct: number;
31
+ viability: string;
32
+ }
33
+ export interface DexAssetRow {
34
+ base: string;
35
+ dexes: {
36
+ dex: string;
37
+ rate: number;
38
+ annualizedPct: number;
39
+ markPrice: number;
40
+ oi: number;
41
+ }[];
42
+ }
43
+ export interface DashboardSnapshot {
44
+ timestamp: string;
45
+ exchanges: {
46
+ name: string;
47
+ balance: ExchangeBalance;
48
+ positions: ExchangePosition[];
49
+ orders: ExchangeOrder[];
50
+ topMarkets: ExchangeMarketInfo[];
51
+ }[];
52
+ totals: {
53
+ equity: number;
54
+ available: number;
55
+ marginUsed: number;
56
+ unrealizedPnl: number;
57
+ positionCount: number;
58
+ orderCount: number;
59
+ };
60
+ arb: {
61
+ opportunities: ArbOpportunity[];
62
+ dexArb: DexArbOpportunity[];
63
+ dexAssets: DexAssetRow[];
64
+ dexNames: string[];
65
+ exchangeStatus: Record<string, string>;
66
+ };
67
+ }
68
+ export interface DashboardOpts {
69
+ port?: number;
70
+ pollInterval?: number;
71
+ arbInterval?: number;
72
+ signal?: AbortSignal;
73
+ }
74
+ /**
75
+ * Start the dashboard HTTP + WebSocket server.
76
+ */
77
+ export declare function startDashboard(exchanges: DashboardExchange[], opts?: DashboardOpts): Promise<{
78
+ port: number;
79
+ close: () => void;
80
+ }>;