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
@@ -0,0 +1,16 @@
1
+ import { type ExecutionRecord } from "./execution-log.js";
2
+ import type { ExchangeAdapter } from "./exchanges/interface.js";
3
+ import type { Network } from "./exchanges/venue-types.js";
4
+ /** Refuse a reduce-only order up front on a venue whose reduce-only orders are gated (uniform CAPABILITY_UNSUPPORTED). */
5
+ export declare function mcpReduceOnlyGate(adapter: ExchangeAdapter): void;
6
+ /** PERP_NETWORK → network. Unset = "mainnet" (documented default); any other value must parse. */
7
+ export declare function resolveMcpNetwork(env: string | undefined): Network;
8
+ /** Pre-send gate for an MCP order: quarantine (non-reduce-only), then local risk limits (never forced). */
9
+ export declare function mcpPreTradeGate(adapter: ExchangeAdapter, p: {
10
+ symbol: string;
11
+ size: number;
12
+ price: number | null;
13
+ reduceOnly: boolean;
14
+ }, log: (msg: string) => void): Promise<void>;
15
+ /** Execution-log entry for an MCP order (source + network recorded in meta). */
16
+ export declare function logMcpExecution(network: Network, record: Omit<ExecutionRecord, "id" | "timestamp" | "dryRun">): ExecutionRecord;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * MCP write-path safety (review N4): the MCP `trade_execute` / `trade_close` tools go through
3
+ * the same gates as `perp trade`:
4
+ *
5
+ * - network: PERP_NETWORK, validated by the same parser as `-n` (unset = mainnet, the
6
+ * documented default — never a silent substitution for an invalid value);
7
+ * - quarantine: a non-reduce-only order on a quarantined symbol is refused (an earlier
8
+ * unknown outcome must be resolved by a human first);
9
+ * - local risk limits: enforceOrderRisk with force=false — there is NO force override over
10
+ * MCP (an agent must not be able to bypass the user's limits);
11
+ * - execution log: every placed / failed order is appended to ~/.perp/executions.jsonl,
12
+ * tagged source "mcp" and the network;
13
+ * - venue gates: a reduce-only order (trade_execute reduceOnly, trade_close) on a venue whose
14
+ * reduce-only orders are currently refused (none today) fails with the uniform
15
+ * CAPABILITY_UNSUPPORTED before any read or send (mcpReduceOnlyGate).
16
+ */
17
+ import { parseNetwork } from "./cli-flags.js";
18
+ import { PerpError } from "./errors.js";
19
+ import { logExecution } from "./execution-log.js";
20
+ import { quarantineEntryFor } from "./bot/quarantine.js";
21
+ import { enforceOrderRisk } from "./trade-validator.js";
22
+ import { reduceOnlyGateOf } from "./exchanges/trading-v2.js";
23
+ /** Refuse a reduce-only order up front on a venue whose reduce-only orders are gated (uniform CAPABILITY_UNSUPPORTED). */
24
+ export function mcpReduceOnlyGate(adapter) {
25
+ const gate = reduceOnlyGateOf(adapter);
26
+ if (gate !== null)
27
+ throw gate;
28
+ }
29
+ /** PERP_NETWORK → network. Unset = "mainnet" (documented default); any other value must parse. */
30
+ export function resolveMcpNetwork(env) {
31
+ if (env === undefined)
32
+ return "mainnet";
33
+ return parseNetwork(env);
34
+ }
35
+ /** Pre-send gate for an MCP order: quarantine (non-reduce-only), then local risk limits (never forced). */
36
+ export async function mcpPreTradeGate(adapter, p, log) {
37
+ if (p.reduceOnly)
38
+ mcpReduceOnlyGate(adapter);
39
+ if (!p.reduceOnly) {
40
+ const q = quarantineEntryFor(p.symbol, log);
41
+ if (q !== null) {
42
+ throw new PerpError("RISK_VIOLATION", `${p.symbol} is quarantined by ${q.strategy}: ${q.reason}`, {
43
+ exchange: adapter.name,
44
+ remediation: `${q.remediation} — only reduce-only orders are accepted until a human clears it (perp strategy quarantine-clear ${p.symbol})`,
45
+ });
46
+ }
47
+ }
48
+ await enforceOrderRisk(adapter, {
49
+ symbol: p.symbol,
50
+ size: p.size,
51
+ ...(p.price !== null ? { price: p.price } : {}),
52
+ reduceOnly: p.reduceOnly,
53
+ force: false,
54
+ });
55
+ }
56
+ /** Execution-log entry for an MCP order (source + network recorded in meta). */
57
+ export function logMcpExecution(network, record) {
58
+ return logExecution({ ...record, dryRun: false, meta: { ...(record.meta ?? {}), source: "mcp", network } });
59
+ }
@@ -1,21 +1,47 @@
1
1
  import { type Network } from "./constants.js";
2
2
  import type { MarketInfo, PriceInfo, Orderbook, Trade, Kline, KlineInterval, AggLevel, AccountInfo, AccountSettings, Position, OrderInfo, MarketOrderParams, LimitOrderParams, StopOrderParams, EditOrderParams, CancelOrderParams, CancelAllOrdersParams, TWAPParams, CancelTWAPParams, TPSLParams, UpdateLeverageParams, WithdrawParams, TransferFundsParams, BatchAction, CreateLakeParams, LakeDepositParams, LakeWithdrawParams } from "./types/index.js";
3
3
  type SignMessageFn = (message: Uint8Array) => Promise<Uint8Array>;
4
+ /** Per-request timeout of a POST (fetch + body read). */
5
+ export declare const PACIFICA_POST_TIMEOUT_MS = 15000;
4
6
  export interface PacificaClientConfig {
5
7
  network?: Network;
6
8
  baseUrl?: string;
7
9
  apiKey?: string;
8
- builderCode?: string;
9
10
  }
10
11
  export declare class PacificaClient {
11
12
  private baseUrl;
12
13
  private apiKey?;
13
- private builderCode;
14
+ /**
15
+ * Agent-wallet pubkey for the current request signer, set by the adapter when an
16
+ * agent wallet (not the master) is signing. When present, signed bodies carry the
17
+ * `agent_wallet` field (with account = master) so Pacifica verifies the signature
18
+ * against the agent key. Undefined for master/PK signers.
19
+ */
20
+ private requestAgentWallet?;
21
+ /** Attach (or clear) the agent wallet used for subsequent signed requests. */
22
+ setRequestAgentWallet(agentWallet?: string): void;
23
+ /** Build a signed POST body, attaching agent_wallet when an agent is the active signer. */
24
+ private signedBody;
14
25
  constructor(config?: PacificaClientConfig);
15
26
  private headers;
16
27
  private get;
28
+ /**
29
+ * Signed POST. Every path except the listing reads (READ_POST_PATHS) is an account write: once
30
+ * the request is on the wire a connection reset / timeout / 5xx / unreadable body leaves the
31
+ * outcome unknown → ORDER_PENDING (sendFailure), never a retryable network error. No connection
32
+ * at all → not sent. A 4xx is the venue's refusal and keeps the plain `POST <path> failed
33
+ * (<status>): <body>` error the adapters map (422 business codes) — except on
34
+ * DEFINITE_4XX_PATHS, where it is a definite ACTION_REJECTED (429 stays the plain error →
35
+ * RATE_LIMITED). A fetch timeout (PACIFICA_POST_TIMEOUT_MS) on a write is ORDER_PENDING too;
36
+ * on a listing read a timeout is TIMEOUT and any other transport failure EXCHANGE_UNREACHABLE.
37
+ */
17
38
  private post;
18
- private addBuilderCode;
39
+ /**
40
+ * `builder_code` is attached only when the caller passes one — the adapter decides
41
+ * (settings builder.pacifica + confirmed approval, src/exchanges/builder-fee.ts).
42
+ * null = no builder code (the default, and always for spot: err 108).
43
+ */
44
+ private withBuilderCode;
19
45
  getInfo(): Promise<MarketInfo[]>;
20
46
  getPrices(): Promise<PriceInfo[]>;
21
47
  getBook(symbol: string, aggLevel?: AggLevel): Promise<Orderbook>;
@@ -26,7 +52,7 @@ export declare class PacificaClient {
26
52
  cursor?: string;
27
53
  }): Promise<unknown>;
28
54
  getAccount(account: string): Promise<AccountInfo>;
29
- getAccountSettings(account: string): Promise<AccountSettings[]>;
55
+ getAccountSettings(account: string): Promise<AccountSettings>;
30
56
  getPositions(account: string): Promise<Position[]>;
31
57
  getOrders(account: string): Promise<OrderInfo[]>;
32
58
  getOrderHistory(account: string, opts?: {
@@ -56,9 +82,9 @@ export declare class PacificaClient {
56
82
  }): Promise<unknown>;
57
83
  getOrderHistoryById(orderId: number): Promise<unknown>;
58
84
  getTWAPOrders(account: string): Promise<unknown[]>;
59
- createMarketOrder(params: MarketOrderParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
60
- createLimitOrder(params: LimitOrderParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
61
- createStopOrder(params: StopOrderParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
85
+ createMarketOrder(params: MarketOrderParams, account: string, signMessage: SignMessageFn, builderCode: string | null): Promise<unknown>;
86
+ createLimitOrder(params: LimitOrderParams, account: string, signMessage: SignMessageFn, builderCode: string | null): Promise<unknown>;
87
+ createStopOrder(params: StopOrderParams, account: string, signMessage: SignMessageFn, builderCode: string | null): Promise<unknown>;
62
88
  editOrder(params: EditOrderParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
63
89
  cancelOrder(params: CancelOrderParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
64
90
  cancelAllOrders(params: CancelAllOrdersParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
@@ -66,23 +92,28 @@ export declare class PacificaClient {
66
92
  symbol: string;
67
93
  order_id: number;
68
94
  }, account: string, signMessage: SignMessageFn): Promise<unknown>;
95
+ /** create_twap_order documents no builder_code, so a TWAP never carries one (builder-fee.ts). */
69
96
  createTWAP(params: TWAPParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
70
97
  cancelTWAP(params: CancelTWAPParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
71
- setTPSL(params: TPSLParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
98
+ setTPSL(params: TPSLParams, account: string, signMessage: SignMessageFn, builderCode: string | null): Promise<unknown>;
72
99
  updateLeverage(params: UpdateLeverageParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
73
100
  updateMarginMode(params: {
74
101
  symbol: string;
75
102
  is_isolated: boolean;
76
103
  }, account: string, signMessage: SignMessageFn): Promise<unknown>;
77
104
  withdraw(params: WithdrawParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
78
- createSubaccount(subaccountName: string, account: string, signMessage: SignMessageFn): Promise<unknown>;
79
105
  listSubaccounts(account: string, signMessage: SignMessageFn): Promise<unknown>;
80
106
  transferFunds(params: TransferFundsParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
81
107
  bindAgentWallet(agentWallet: string, account: string, signMessage: SignMessageFn): Promise<unknown>;
82
108
  listAgentWallets(account: string, signMessage: SignMessageFn): Promise<unknown>;
83
109
  revokeAgentWallet(agentWallet: string, account: string, signMessage: SignMessageFn): Promise<unknown>;
84
110
  revokeAllAgentWallets(account: string, signMessage: SignMessageFn): Promise<unknown>;
85
- createApiKey(name: string, maxFeeRate: string, account: string, signMessage: SignMessageFn): Promise<unknown>;
111
+ /**
112
+ * API Config Key (rate-limit key). Docs (api/rate-limits/api-config-keys): the request body is
113
+ * account / signature / timestamp / expiry_window only; python-sdk rest/api_config_keys.py signs
114
+ * type "create_api_key" with `signature_payload = {}`.
115
+ */
116
+ createApiKey(account: string, signMessage: SignMessageFn): Promise<unknown>;
86
117
  listApiKeys(account: string, signMessage: SignMessageFn): Promise<unknown>;
87
118
  revokeApiKey(apiKey: string, account: string, signMessage: SignMessageFn): Promise<unknown>;
88
119
  createLake(params: CreateLakeParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
@@ -1,14 +1,49 @@
1
- import { BUILDER_CODE, getNetworkConfig } from "./constants.js";
2
- import { buildSignedRequest } from "./signing.js";
1
+ import { getNetworkConfig } from "./constants.js";
2
+ import { buildSignedRequest, buildAgentSignedRequest } from "./signing.js";
3
+ import { PerpError, extractErrorMessage, orderNotSent, orderOutcomeUnknown, sendFailure } from "../errors.js";
4
+ import { invalidateCache } from "../cache.js";
5
+ /** Per-request timeout of a POST (fetch + body read). */
6
+ export const PACIFICA_POST_TIMEOUT_MS = 15_000;
7
+ /**
8
+ * Account-setting writes whose HTTP 4xx is the venue's explicit refusal (nothing changed).
9
+ * docs.pacifica.fi (api-documentation/api/rest-api):
10
+ * account/update-leverage: "Status 400: Invalid request parameters" — {"error": "Invalid leverage", "code": 400}
11
+ * orders/create-position-tp-sl: "Status 400: Bad request" — {"error": "Position not found", "code": 400}
12
+ * Both pages list only "Status 500: Internal server error" besides.
13
+ */
14
+ const DEFINITE_4XX_PATHS = new Set(["/positions/tpsl", "/account/leverage"]);
15
+ /** POST endpoints that only read (signed listings): a failure there is an ordinary read error. */
16
+ const READ_POST_PATHS = new Set(["/account/subaccount/list", "/agent/list", "/account/api_keys"]);
3
17
  export class PacificaClient {
4
18
  baseUrl;
5
19
  apiKey;
6
- builderCode;
20
+ /**
21
+ * Agent-wallet pubkey for the current request signer, set by the adapter when an
22
+ * agent wallet (not the master) is signing. When present, signed bodies carry the
23
+ * `agent_wallet` field (with account = master) so Pacifica verifies the signature
24
+ * against the agent key. Undefined for master/PK signers.
25
+ */
26
+ requestAgentWallet;
27
+ /** Attach (or clear) the agent wallet used for subsequent signed requests. */
28
+ setRequestAgentWallet(agentWallet) {
29
+ this.requestAgentWallet = agentWallet;
30
+ }
31
+ /** Build a signed POST body, attaching agent_wallet when an agent is the active signer. */
32
+ async signedBody(operationType, payload, account, signMessage) {
33
+ try {
34
+ return await (this.requestAgentWallet
35
+ ? buildAgentSignedRequest(operationType, payload, account, this.requestAgentWallet, signMessage)
36
+ : buildSignedRequest(operationType, payload, account, signMessage));
37
+ }
38
+ catch (e) {
39
+ // Signing failed before any request was sent: definitely not placed (never ORDER_PENDING).
40
+ throw orderNotSent("pacifica", operationType, e);
41
+ }
42
+ }
7
43
  constructor(config = {}) {
8
44
  this.baseUrl =
9
45
  config.baseUrl || getNetworkConfig(config.network).restUrl;
10
46
  this.apiKey = config.apiKey;
11
- this.builderCode = config.builderCode || BUILDER_CODE;
12
47
  }
13
48
  headers() {
14
49
  const h = { "Content-Type": "application/json" };
@@ -36,23 +71,77 @@ export class PacificaClient {
36
71
  }
37
72
  return json;
38
73
  }
74
+ /**
75
+ * Signed POST. Every path except the listing reads (READ_POST_PATHS) is an account write: once
76
+ * the request is on the wire a connection reset / timeout / 5xx / unreadable body leaves the
77
+ * outcome unknown → ORDER_PENDING (sendFailure), never a retryable network error. No connection
78
+ * at all → not sent. A 4xx is the venue's refusal and keeps the plain `POST <path> failed
79
+ * (<status>): <body>` error the adapters map (422 business codes) — except on
80
+ * DEFINITE_4XX_PATHS, where it is a definite ACTION_REJECTED (429 stays the plain error →
81
+ * RATE_LIMITED). A fetch timeout (PACIFICA_POST_TIMEOUT_MS) on a write is ORDER_PENDING too;
82
+ * on a listing read a timeout is TIMEOUT and any other transport failure EXCHANGE_UNREACHABLE.
83
+ */
39
84
  async post(path, body) {
40
- const res = await fetch(`${this.baseUrl}${path}`, {
41
- method: "POST",
42
- headers: this.headers(),
43
- body: JSON.stringify(body),
44
- });
45
- if (!res.ok) {
46
- const text = await res.text();
47
- throw new Error(`POST ${path} failed (${res.status}): ${text}`);
85
+ const write = !READ_POST_PATHS.has(path);
86
+ const what = `POST ${path}`;
87
+ const remediation = `Do NOT resubmit — the request may have been executed. Check open orders, positions and balances first: perp -e pacifica account orders / portfolio`;
88
+ try {
89
+ let res;
90
+ let text;
91
+ try {
92
+ res = await fetch(`${this.baseUrl}${path}`, {
93
+ method: "POST",
94
+ headers: this.headers(),
95
+ body: JSON.stringify(body),
96
+ signal: AbortSignal.timeout(PACIFICA_POST_TIMEOUT_MS),
97
+ });
98
+ text = await res.text();
99
+ }
100
+ catch (e) {
101
+ if (write)
102
+ throw sendFailure("pacifica", what, e, remediation);
103
+ // Signed listing read: nothing can have been executed — an ordinary retryable read failure.
104
+ const timedOut = e instanceof Error && (e.name === "TimeoutError" || e.name === "AbortError");
105
+ throw new PerpError(timedOut ? "TIMEOUT" : "EXCHANGE_UNREACHABLE", `Pacifica ${what} ${timedOut ? `timed out after ${PACIFICA_POST_TIMEOUT_MS} ms` : `failed: ${extractErrorMessage(e)}`} (read-only listing; nothing was changed)`, {
106
+ exchange: "pacifica",
107
+ remediation: "Retry; if it keeps failing check the network and Pacifica's status",
108
+ });
109
+ }
110
+ if (write && res.status >= 500) {
111
+ throw orderOutcomeUnknown("pacifica", what, new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`), remediation);
112
+ }
113
+ if (!res.ok) {
114
+ if (DEFINITE_4XX_PATHS.has(path) && res.status >= 400 && res.status < 500 && res.status !== 429) {
115
+ throw new PerpError("ACTION_REJECTED", `Pacifica ${what} refused (HTTP ${res.status}, nothing was changed): ${text.slice(0, 300)}`, {
116
+ exchange: "pacifica",
117
+ httpStatus: res.status,
118
+ remediation: "The venue rejected the request — fix the parameters named in the message and retry",
119
+ });
120
+ }
121
+ throw new Error(`POST ${path} failed (${res.status}): ${text}`);
122
+ }
123
+ try {
124
+ return JSON.parse(text);
125
+ }
126
+ catch (e) {
127
+ if (!write)
128
+ throw e;
129
+ throw orderOutcomeUnknown("pacifica", what, new Error(`unreadable response (HTTP ${res.status}): ${text.slice(0, 200)}`), remediation);
130
+ }
48
131
  }
49
- return res.json();
50
- }
51
- addBuilderCode(payload) {
52
- if (this.builderCode) {
53
- return { ...payload, builder_code: this.builderCode };
132
+ finally {
133
+ // Every POST is an account write (order / cancel / TP/SL / leverage / transfer): drop the
134
+ // shared acct: disk cache so no later read serves the pre-write positions / orders.
135
+ invalidateCache("acct");
54
136
  }
55
- return payload;
137
+ }
138
+ /**
139
+ * `builder_code` is attached only when the caller passes one — the adapter decides
140
+ * (settings builder.pacifica + confirmed approval, src/exchanges/builder-fee.ts).
141
+ * null = no builder code (the default, and always for spot: err 108).
142
+ */
143
+ withBuilderCode(payload, builderCode) {
144
+ return builderCode === null ? payload : { ...payload, builder_code: builderCode };
56
145
  }
57
146
  // ==================== PUBLIC ENDPOINTS ====================
58
147
  async getInfo() {
@@ -158,135 +247,150 @@ export class PacificaClient {
158
247
  return this.get("/orders/twap", { account });
159
248
  }
160
249
  // ==================== AUTHENTICATED POST ENDPOINTS ====================
161
- async createMarketOrder(params, account, signMessage) {
162
- const payload = this.addBuilderCode({ ...params });
163
- const body = await buildSignedRequest("create_market_order", payload, account, signMessage);
250
+ async createMarketOrder(params, account, signMessage, builderCode) {
251
+ const payload = this.withBuilderCode({ ...params }, builderCode);
252
+ const body = await this.signedBody("create_market_order", payload, account, signMessage);
164
253
  return this.post("/orders/create_market", body);
165
254
  }
166
- async createLimitOrder(params, account, signMessage) {
167
- const payload = this.addBuilderCode({ ...params });
168
- const body = await buildSignedRequest("create_order", payload, account, signMessage);
255
+ async createLimitOrder(params, account, signMessage, builderCode) {
256
+ const payload = this.withBuilderCode({ ...params }, builderCode);
257
+ const body = await this.signedBody("create_order", payload, account, signMessage);
169
258
  return this.post("/orders/create", body);
170
259
  }
171
- async createStopOrder(params, account, signMessage) {
172
- const body = await buildSignedRequest("create_stop_order", params, account, signMessage);
260
+ async createStopOrder(params, account, signMessage, builderCode) {
261
+ // builder_code applies to every order-creation endpoint, stop orders included (builder-program docs).
262
+ const payload = this.withBuilderCode({ ...params }, builderCode);
263
+ const body = await this.signedBody("create_stop_order", payload, account, signMessage);
173
264
  return this.post("/orders/stop/create", body);
174
265
  }
175
266
  async editOrder(params, account, signMessage) {
176
- const body = await buildSignedRequest("edit_order", params, account, signMessage);
267
+ const body = await this.signedBody("edit_order", params, account, signMessage);
177
268
  return this.post("/orders/edit", body);
178
269
  }
179
270
  async cancelOrder(params, account, signMessage) {
180
- const body = await buildSignedRequest("cancel_order", params, account, signMessage);
271
+ const body = await this.signedBody("cancel_order", params, account, signMessage);
181
272
  return this.post("/orders/cancel", body);
182
273
  }
183
274
  async cancelAllOrders(params, account, signMessage) {
184
- const body = await buildSignedRequest("cancel_all_orders", params, account, signMessage);
275
+ const body = await this.signedBody("cancel_all_orders", params, account, signMessage);
185
276
  return this.post("/orders/cancel_all", body);
186
277
  }
187
278
  async cancelStopOrder(params, account, signMessage) {
188
- const body = await buildSignedRequest("cancel_stop_order", params, account, signMessage);
279
+ const body = await this.signedBody("cancel_stop_order", params, account, signMessage);
189
280
  return this.post("/orders/stop/cancel", body);
190
281
  }
282
+ /** create_twap_order documents no builder_code, so a TWAP never carries one (builder-fee.ts). */
191
283
  async createTWAP(params, account, signMessage) {
192
- const payload = this.addBuilderCode({ ...params });
193
- const body = await buildSignedRequest("create_twap_order", payload, account, signMessage);
284
+ const body = await this.signedBody("create_twap_order", { ...params }, account, signMessage);
194
285
  return this.post("/orders/twap/create", body);
195
286
  }
196
287
  async cancelTWAP(params, account, signMessage) {
197
- const body = await buildSignedRequest("cancel_twap_order", params, account, signMessage);
288
+ const body = await this.signedBody("cancel_twap_order", params, account, signMessage);
198
289
  return this.post("/orders/twap/cancel", body);
199
290
  }
200
- async setTPSL(params, account, signMessage) {
201
- const body = await buildSignedRequest("set_position_tpsl", params, account, signMessage);
291
+ async setTPSL(params, account, signMessage, builderCode) {
292
+ // Position TP/SL creates orders and therefore takes builder_code too (POST /positions/tpsl).
293
+ const payload = this.withBuilderCode({ ...params }, builderCode);
294
+ const body = await this.signedBody("set_position_tpsl", payload, account, signMessage);
202
295
  return this.post("/positions/tpsl", body);
203
296
  }
204
297
  async updateLeverage(params, account, signMessage) {
205
- const body = await buildSignedRequest("update_leverage", params, account, signMessage);
298
+ const body = await this.signedBody("update_leverage", params, account, signMessage);
206
299
  return this.post("/account/leverage", body);
207
300
  }
208
301
  async updateMarginMode(params, account, signMessage) {
209
- const body = await buildSignedRequest("update_margin_mode", params, account, signMessage);
302
+ const body = await this.signedBody("update_margin_mode", params, account, signMessage);
210
303
  return this.post("/account/margin", body);
211
304
  }
212
305
  async withdraw(params, account, signMessage) {
213
- const body = await buildSignedRequest("withdraw", params, account, signMessage);
306
+ const body = await this.signedBody("withdraw", params, account, signMessage);
214
307
  return this.post("/account/withdraw", body);
215
308
  }
216
309
  // ==================== SUBACCOUNT ====================
217
- async createSubaccount(subaccountName, account, signMessage) {
218
- // Step 1: Initiate
219
- const initiateBody = await buildSignedRequest("subaccount_initiate", { subaccount_name: subaccountName }, account, signMessage);
220
- await this.post("/account/subaccount/create", initiateBody);
221
- // Step 2: Confirm
222
- const confirmBody = await buildSignedRequest("subaccount_confirm", { subaccount_name: subaccountName }, account, signMessage);
223
- return this.post("/account/subaccount/create", confirmBody);
224
- }
310
+ // POST /account/subaccount/create is not implemented. Docs (rest-api/subaccounts/create-subaccount):
311
+ // "Both the main account and subaccount must sign to establish the relationship." — the body is
312
+ // {main_account, subaccount, main_signature, sub_signature, timestamp, expiry_window}; the
313
+ // subaccount keypair signs {account: <main>} ("subaccount_initiate") and the main account signs
314
+ // {signature: <sub_signature>} ("subaccount_confirm") (python-sdk rest/create_subaccount.py).
315
+ // perp-cli has no second (subaccount) keypair source, so `manage sub create` is gated.
225
316
  async listSubaccounts(account, signMessage) {
226
- const body = await buildSignedRequest("list_subaccounts", {}, account, signMessage);
317
+ const body = await this.signedBody("list_subaccounts", {}, account, signMessage);
227
318
  return this.post("/account/subaccount/list", body);
228
319
  }
229
320
  async transferFunds(params, account, signMessage) {
230
- const body = await buildSignedRequest("transfer_funds", params, account, signMessage);
321
+ const body = await this.signedBody("transfer_funds", params, account, signMessage);
231
322
  return this.post("/account/subaccount/transfer", body);
232
323
  }
233
324
  // ==================== AGENT WALLET ====================
234
325
  async bindAgentWallet(agentWallet, account, signMessage) {
235
- const body = await buildSignedRequest("bind_agent_wallet", { agent_wallet: agentWallet }, account, signMessage);
326
+ const body = await this.signedBody("bind_agent_wallet", { agent_wallet: agentWallet }, account, signMessage);
236
327
  return this.post("/agent/bind", body);
237
328
  }
238
329
  async listAgentWallets(account, signMessage) {
239
- const body = await buildSignedRequest("list_agent_wallets", {}, account, signMessage);
330
+ const body = await this.signedBody("list_agent_wallets", {}, account, signMessage);
240
331
  return this.post("/agent/list", body);
241
332
  }
242
333
  async revokeAgentWallet(agentWallet, account, signMessage) {
243
- const body = await buildSignedRequest("revoke_agent_wallet", { agent_wallet: agentWallet }, account, signMessage);
334
+ const body = await this.signedBody("revoke_agent_wallet", { agent_wallet: agentWallet }, account, signMessage);
244
335
  return this.post("/agent/revoke", body);
245
336
  }
246
337
  async revokeAllAgentWallets(account, signMessage) {
247
- const body = await buildSignedRequest("revoke_all_agent_wallets", {}, account, signMessage);
338
+ const body = await this.signedBody("revoke_all_agent_wallets", {}, account, signMessage);
248
339
  return this.post("/agent/revoke_all", body);
249
340
  }
250
341
  // ==================== API KEYS ====================
251
- async createApiKey(name, maxFeeRate, account, signMessage) {
252
- const body = await buildSignedRequest("create_api_key", { name, max_fee_rate: maxFeeRate }, account, signMessage);
342
+ /**
343
+ * API Config Key (rate-limit key). Docs (api/rate-limits/api-config-keys): the request body is
344
+ * account / signature / timestamp / expiry_window only; python-sdk rest/api_config_keys.py signs
345
+ * type "create_api_key" with `signature_payload = {}`.
346
+ */
347
+ async createApiKey(account, signMessage) {
348
+ const body = await this.signedBody("create_api_key", {}, account, signMessage);
253
349
  return this.post("/account/api_keys/create", body);
254
350
  }
255
351
  async listApiKeys(account, signMessage) {
256
- const body = await buildSignedRequest("list_api_keys", {}, account, signMessage);
352
+ const body = await this.signedBody("list_api_keys", {}, account, signMessage);
257
353
  return this.post("/account/api_keys", body);
258
354
  }
259
355
  async revokeApiKey(apiKey, account, signMessage) {
260
- const body = await buildSignedRequest("revoke_api_key", { api_key: apiKey }, account, signMessage);
356
+ const body = await this.signedBody("revoke_api_key", { api_key: apiKey }, account, signMessage);
261
357
  return this.post("/account/api_keys/revoke", body);
262
358
  }
263
359
  // ==================== LAKE ====================
264
360
  async createLake(params, account, signMessage) {
265
- const body = await buildSignedRequest("create_lake", params, account, signMessage);
361
+ // Optional fields are omitted, not sent as null (the signed payload must equal the body).
362
+ const payload = {};
363
+ if (params.manager !== null)
364
+ payload.manager = params.manager;
365
+ if (params.nickname !== null)
366
+ payload.nickname = params.nickname;
367
+ if (params.initial_deposit !== null)
368
+ payload.initial_deposit = params.initial_deposit;
369
+ const body = await this.signedBody("create_lake", payload, account, signMessage);
266
370
  return this.post("/lake/create", body);
267
371
  }
268
372
  async depositToLake(params, account, signMessage) {
269
- const body = await buildSignedRequest("deposit_to_lake", params, account, signMessage);
373
+ const body = await this.signedBody("deposit_to_lake", params, account, signMessage);
270
374
  return this.post("/lake/deposit", body);
271
375
  }
272
376
  async withdrawFromLake(params, account, signMessage) {
273
- const body = await buildSignedRequest("withdraw_from_lake", params, account, signMessage);
377
+ const body = await this.signedBody("withdraw_from_lake", params, account, signMessage);
274
378
  return this.post("/lake/withdraw", body);
275
379
  }
276
380
  // ==================== BUILDER CODE ====================
277
381
  async approveBuilderCode(params, account, signMessage) {
278
- const body = await buildSignedRequest("approve_builder_code", params, account, signMessage);
382
+ const body = await this.signedBody("approve_builder_code", params, account, signMessage);
279
383
  return this.post("/account/builder_codes/approve", body);
280
384
  }
281
385
  async revokeBuilderCode(params, account, signMessage) {
282
- const body = await buildSignedRequest("revoke_builder_code", params, account, signMessage);
386
+ const body = await this.signedBody("revoke_builder_code", params, account, signMessage);
283
387
  return this.post("/account/builder_codes/revoke", body);
284
388
  }
285
389
  async getBuilderApprovals(account) {
286
390
  return this.get("/account/builder_codes/approvals", { account });
287
391
  }
288
392
  async updateBuilderFeeRate(params, account, signMessage) {
289
- const body = await buildSignedRequest("update_builder_code_fee_rate", params, account, signMessage);
393
+ const body = await this.signedBody("update_builder_code_fee_rate", params, account, signMessage);
290
394
  return this.post("/builder/update_fee_rate", body);
291
395
  }
292
396
  async getBuilderOverview(account) {
@@ -300,7 +404,7 @@ export class PacificaClient {
300
404
  }
301
405
  // ==================== REFERRAL ====================
302
406
  async claimReferralCode(params, account, signMessage) {
303
- const body = await buildSignedRequest("claim_referral_code", params, account, signMessage);
407
+ const body = await this.signedBody("claim_referral_code", params, account, signMessage);
304
408
  return this.post("/referral/user/code/claim", body);
305
409
  }
306
410
  // ==================== BATCH ====================
@@ -12,7 +12,6 @@ export declare const TESTNET_PACIFICA_VAULT = "5SDFdHZGTZbyRYu54CgmRkCGnPHC5pYaN
12
12
  export declare const TESTNET_USDC_MINT = "USDPqRbLidFGufty2s3oizmDEKdqx7ePTqzDMbf5ZKM";
13
13
  export declare const DEFAULT_EXPIRY_WINDOW = 5000;
14
14
  export declare const USDC_DECIMALS = 6;
15
- export declare const BUILDER_CODE = "PERPCLI";
16
15
  export type Network = "mainnet" | "testnet";
17
16
  export declare function getNetworkConfig(network?: Network): {
18
17
  restUrl: string;
@@ -19,11 +19,6 @@ export const TESTNET_USDC_MINT = "USDPqRbLidFGufty2s3oizmDEKdqx7ePTqzDMbf5ZKM";
19
19
  export const DEFAULT_EXPIRY_WINDOW = 5000; // 5 seconds
20
20
  // USDC decimals
21
21
  export const USDC_DECIMALS = 6;
22
- // Builder code — 0% fee, used for Pacifica hackathon tracking
23
- // The actual 3-way fallback (env → settings.referralCodes.pacifica → "PERPCLI") is
24
- // resolved in index.ts before constructing PacificaAdapter. This constant is only
25
- // a last-resort default for direct PacificaClient usage without a builderCode arg.
26
- export const BUILDER_CODE = "PERPCLI";
27
22
  export function getNetworkConfig(network = "mainnet") {
28
23
  if (network === "testnet") {
29
24
  return {
@@ -4,11 +4,38 @@ import { type Network } from "./constants.js";
4
4
  * Build a Pacifica deposit instruction.
5
5
  *
6
6
  * @param userPubkey - User's wallet public key
7
- * @param amount - Amount in USDC (e.g., 100 for $100)
7
+ * @param amount - Amount in USDC as a plain decimal string with at most 6 decimals (e.g. "100", "99.5");
8
+ * scaled exactly (no float math). Anything else → INVALID_PARAMS.
8
9
  * @param network - mainnet or testnet
9
10
  */
10
- export declare function buildDepositInstruction(userPubkey: PublicKey, amount: number, network?: Network): Promise<TransactionInstruction>;
11
+ export declare function buildDepositInstruction(userPubkey: PublicKey, amount: string, network?: Network): Promise<TransactionInstruction>;
11
12
  /**
12
13
  * Build and send a deposit transaction.
13
14
  */
14
- export declare function deposit(connection: Connection, userPubkey: PublicKey, amount: number, signTransaction: (tx: Transaction) => Promise<Transaction>, network?: Network): Promise<TransactionSignature>;
15
+ export declare function deposit(connection: Connection, userPubkey: PublicKey, amount: string, signTransaction: (tx: Transaction) => Promise<Transaction>, network?: Network): Promise<TransactionSignature>;
16
+ /** Highest relayer fee (USDC) the CLI signs for a relayed deposit. Above it the transaction is refused. */
17
+ export declare const PACIFICA_RELAY_FEE_CAP_USDC = 1;
18
+ /**
19
+ * Verify a RELAYER-BUILT transaction before the master Solana key signs it. The relayer is an
20
+ * untrusted HTTP service (PERP_RELAYER_URL): whatever it returns is signed only when it is
21
+ * exactly
22
+ * - ONE pinned Pacifica deposit instruction — the program id, every account (user, user USDC
23
+ * ATA, central state, vault, mint, …, in order, same signer / writable flags) and the data
24
+ * `buildDepositInstruction` would produce for `declared.netAmount`;
25
+ * - any number of ComputeBudget instructions (they move no funds);
26
+ * - at most ONE relayer fee transfer: an SPL Token Transfer / TransferChecked of exactly
27
+ * `declared.fee` USDC from the user's USDC account, authority = user, to the token account the
28
+ * build response declared (`feeRecipient`), with fee ≤ PACIFICA_RELAY_FEE_CAP_USDC;
29
+ * and fee + netAmount equals the amount the user asked to deposit, and the fee payer is not the
30
+ * user (the relayer sponsors gas). Anything else → POLICY_DENIED, nothing is signed.
31
+ */
32
+ export declare function assertRelayedDepositTransaction(tx: Transaction, ctx: {
33
+ user: PublicKey;
34
+ requestedAmount: string;
35
+ network: Network;
36
+ declared: {
37
+ fee: number;
38
+ netAmount: number;
39
+ feeRecipient: string | null;
40
+ };
41
+ }): Promise<void>;