perp-cli 0.3.9 → 0.3.11

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.
@@ -70,12 +70,16 @@ export class LighterAdapter {
70
70
  : json.accounts[0].account_index;
71
71
  }
72
72
  // Auto-generate API key if we have PK but no API key and account exists
73
+ let signerReady = false;
73
74
  if (!this._apiKey && this._accountIndex >= 0) {
74
75
  try {
75
76
  const autoKeyIndex = 2; // default for auto-setup
76
77
  const { privateKey: apiKey } = await this.setupApiKey(autoKeyIndex);
77
78
  this._apiKey = apiKey;
78
79
  this._apiKeyIndex = autoKeyIndex;
80
+ // setupApiKey already configured the static WASM client — reuse it
81
+ this._signer = LighterAdapter._wasmClient;
82
+ signerReady = true;
79
83
  // Save to .env for future use
80
84
  try {
81
85
  const { setEnvVar } = await import("../commands/init.js");
@@ -91,18 +95,19 @@ export class LighterAdapter {
91
95
  console.error(`[lighter] API key auto-setup failed: ${msg}. Trading will be read-only. Run 'perp -e lighter manage setup-api-key' to retry.`);
92
96
  }
93
97
  }
94
- // Initialize signer for trading if we have an API key
95
- if (this._apiKey) {
96
- const { WasmSignerClient } = await import("lighter-ts-sdk");
97
- this._signer = new WasmSignerClient({});
98
- await this._signer.initialize();
99
- await this._signer.createClient({
98
+ // Initialize signer for trading if we have an API key (reuse singleton WASM client)
99
+ if (this._apiKey && !signerReady) {
100
+ const client = await LighterAdapter.getWasmClient();
101
+ await client.createClient({
100
102
  url: this._baseUrl,
101
103
  privateKey: this._apiKey,
102
104
  chainId: this._chainId,
103
105
  apiKeyIndex: this._apiKeyIndex,
104
106
  accountIndex: this._accountIndex,
105
107
  });
108
+ this._signer = client;
109
+ }
110
+ if (this._apiKey) {
106
111
  this._readOnly = false;
107
112
  }
108
113
  // Build symbol → marketIndex map + decimals from orderBookDetails
@@ -768,11 +773,22 @@ export class LighterAdapter {
768
773
  static async getWasmClient() {
769
774
  if (LighterAdapter._wasmClient)
770
775
  return LighterAdapter._wasmClient;
771
- const { WasmSignerClient } = await import("lighter-ts-sdk");
772
- const client = new WasmSignerClient({});
773
- await client.initialize();
774
- LighterAdapter._wasmClient = client;
775
- return client;
776
+ // Prevent Go WASM runtime from killing the process on panic
777
+ const origExit = process.exit;
778
+ process.exit = ((code) => {
779
+ process.exit = origExit; // restore immediately
780
+ throw new Error(`Lighter WASM runtime exited with code ${code}`);
781
+ });
782
+ try {
783
+ const { WasmSignerClient } = await import("lighter-ts-sdk");
784
+ const client = new WasmSignerClient({});
785
+ await client.initialize();
786
+ LighterAdapter._wasmClient = client;
787
+ return client;
788
+ }
789
+ finally {
790
+ process.exit = origExit;
791
+ }
776
792
  }
777
793
  /**
778
794
  * Generate a new Lighter API key pair using the WASM signer.
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { config } from "dotenv";
3
3
  import { resolve } from "path";
4
+ import { createRequire } from "node:module";
4
5
  // Load ~/.perp/.env first (global), then CWD .env (overrides)
5
6
  config({ path: resolve(process.env.HOME || "~", ".perp", ".env") });
6
7
  config();
@@ -41,6 +42,8 @@ import { registerInitCommand, EXCHANGE_ENV_MAP, validateKey } from "./commands/i
41
42
  import { registerEnvCommands } from "./commands/env.js";
42
43
  import { loadSettings, saveSettings } from "./settings.js";
43
44
  import { setSharedApiNetwork } from "./shared-api.js";
45
+ const _require = createRequire(import.meta.url);
46
+ const _pkg = _require("../package.json");
44
47
  const program = new Command();
45
48
  // Resolve default exchange from settings (fallback: "pacifica")
46
49
  const _settings = loadSettings();
@@ -48,7 +51,7 @@ const _defaultExchange = _settings.defaultExchange || "pacifica";
48
51
  program
49
52
  .name("perp")
50
53
  .description("Multi-DEX Perpetual Futures CLI (Pacifica, Hyperliquid, Lighter)")
51
- .version("0.3.7")
54
+ .version(_pkg.version)
52
55
  .option("-e, --exchange <exchange>", `Exchange: pacifica, hyperliquid, lighter (default: ${_defaultExchange})`, _defaultExchange)
53
56
  .option("-n, --network <network>", "Network: mainnet or testnet", "mainnet")
54
57
  .option("-k, --private-key <key>", "Private key")
@@ -7,6 +7,7 @@
7
7
  * Adapters are created lazily from environment variables.
8
8
  */
9
9
  import "dotenv/config";
10
+ import { createRequire } from "node:module";
10
11
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
13
  import { z } from "zod";
@@ -56,7 +57,9 @@ function err(error, meta) {
56
57
  return JSON.stringify({ ok: false, error, meta }, null, 2);
57
58
  }
58
59
  // ── MCP Server ──
59
- const server = new McpServer({ name: "perp-cli", version: "0.3.7" }, { capabilities: { tools: {}, resources: {} } });
60
+ const _require = createRequire(import.meta.url);
61
+ const _pkg = _require("../package.json");
62
+ const server = new McpServer({ name: "perp-cli", version: _pkg.version }, { capabilities: { tools: {}, resources: {} } });
60
63
  // ============================================================
61
64
  // Market Data tools (read-only, no private key needed)
62
65
  // ============================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perp-cli",
3
- "version": "0.3.9",
3
+ "version": "0.3.11",
4
4
  "description": "Multi-DEX Perpetual Futures CLI - Pacifica, Hyperliquid, Lighter",
5
5
  "bin": {
6
6
  "perp": "./dist/index.js",
@@ -5,7 +5,7 @@ allowed-tools: "Bash(perp:*), Bash(npx perp-cli:*), Bash(npx -y perp-cli:*)"
5
5
  license: MIT
6
6
  metadata:
7
7
  author: hypurrquant
8
- version: "0.3.9"
8
+ version: "0.3.10"
9
9
  mcp-server: perp-cli
10
10
  ---
11
11
 
@@ -34,14 +34,6 @@ npm install -g perp-cli
34
34
 
35
35
  CRITICAL: Do NOT use `perp init` — it is interactive and will hang.
36
36
 
37
- **If user provides a private key:**
38
- ```bash
39
- perp --json wallet set hl <KEY> # Hyperliquid (aliases: hl, hyperliquid)
40
- perp --json wallet set pac <KEY> # Pacifica (aliases: pac, pacifica)
41
- perp --json wallet set lt <KEY> # Lighter (aliases: lt, lighter)
42
- perp --json wallet set hl <KEY> --default # also set as default exchange
43
- ```
44
-
45
37
  **If user needs a new wallet:**
46
38
  ```bash
47
39
  perp --json wallet generate evm # creates EVM wallet for Hyperliquid + Lighter
@@ -49,6 +41,41 @@ perp --json wallet generate solana # creates Solana wallet for Pacific
49
41
  # IMPORTANT: Tell user the generated address so they can fund it with USDC!
50
42
  ```
51
43
 
44
+ ### Hyperliquid setup
45
+ ```bash
46
+ perp --json wallet set hl <EVM_PRIVATE_KEY> # register EVM key → ready to trade immediately
47
+ perp --json wallet show # verify
48
+ perp --json -e hl account info # check balance
49
+ ```
50
+ No extra steps. Key is saved as `HL_PRIVATE_KEY` in .env.
51
+
52
+ ### Pacifica setup
53
+ ```bash
54
+ perp --json wallet set pac <SOLANA_PRIVATE_KEY> # register Solana key → ready to trade immediately
55
+ perp --json wallet show # verify
56
+ perp --json -e pac account info # check balance
57
+ ```
58
+ No extra steps. Key is saved as `PACIFICA_PRIVATE_KEY` in .env.
59
+
60
+ ### Lighter setup (API key auto-generated on registration)
61
+ ```bash
62
+ perp --json wallet set lt <EVM_PRIVATE_KEY> # register EVM key
63
+ # → AUTOMATICALLY generates Lighter API key via on-chain tx
64
+ # → Saves to .env: LIGHTER_PRIVATE_KEY, LIGHTER_API_KEY, LIGHTER_ACCOUNT_INDEX, LIGHTER_API_KEY_INDEX
65
+ # → No manual API key creation needed. Do NOT ask the user to visit the Lighter website.
66
+ perp --json wallet show # verify
67
+ perp --json -e lighter account info # check balance
68
+ ```
69
+ Same EVM key can be used for both Hyperliquid and Lighter:
70
+ ```bash
71
+ perp --json wallet set hl <KEY> # same key
72
+ perp --json wallet set lt <KEY> # same key, different exchange binding
73
+ ```
74
+ If API key auto-setup fails (rare, e.g. no ETH for gas on Lighter chain):
75
+ ```bash
76
+ perp --json -e lighter manage setup-api-key # manual retry
77
+ ```
78
+
52
79
  **Verify setup (ALWAYS do this after any wallet command):**
53
80
  ```bash
54
81
  perp --json wallet show
@@ -75,13 +102,39 @@ Symbols are auto-resolved across exchanges. Use bare symbols (e.g., `BTC`, `SOL`
75
102
  ### Common operations
76
103
  ```bash
77
104
  perp --json wallet show # check configured wallets
78
- perp --json -e hl account info # balance & margin
79
- perp --json -e hl account positions # open positions
80
- perp --json -e hl market list # available markets
81
- perp --json -e hl market mid BTC # BTC mid price
82
- perp --json arb scan --min 5 # find funding arb opportunities (>5bps spread)
83
105
  perp --json portfolio # unified multi-exchange view
106
+ perp --json arb scan --min 5 # find funding arb opportunities (>5bps spread)
107
+ ```
108
+
109
+ ### Per-exchange commands (ALL 3 exchanges use the SAME commands)
110
+ Every command below works on ALL exchanges. Just change `-e`:
111
+ ```bash
112
+ # Account
113
+ perp --json -e hl account info # Hyperliquid balance & margin
114
+ perp --json -e pac account info # Pacifica balance & margin
115
+ perp --json -e lighter account info # Lighter balance & margin
116
+ perp --json -e <EX> account positions # open positions
117
+
118
+ # Market data
119
+ perp --json -e <EX> market list # available markets
120
+ perp --json -e <EX> market mid <SYM> # mid price
121
+ perp --json -e <EX> market book <SYM> # orderbook depth
122
+
123
+ # Trading (same syntax on ALL exchanges)
124
+ perp --json -e <EX> trade leverage <SYM> <N> --isolated # set leverage
125
+ perp --json -e <EX> trade market <SYM> buy <SIZE> # market buy
126
+ perp --json -e <EX> trade market <SYM> sell <SIZE> # market sell
127
+ perp --json -e <EX> trade close <SYM> # close position
128
+ perp --json -e <EX> trade check <SYM> <SIDE> <SIZE> --leverage <L> # pre-flight
129
+
130
+ # Deposit / Withdraw
131
+ perp --json deposit hyperliquid <AMOUNT> # deposit to HL
132
+ perp --json deposit pacifica <AMOUNT> # deposit to Pacifica
133
+ perp --json deposit lighter info # show Lighter deposit routes
134
+ perp --json deposit lighter cctp arb <AMOUNT> # deposit to Lighter via CCTP
135
+ perp --json withdraw <EX> <AMOUNT> # withdraw from exchange
84
136
  ```
137
+ **All 3 exchanges are fully operational.** Do NOT say any exchange "requires additional setup" or "is not available" — if `wallet show` shows it configured, it's ready to trade.
85
138
 
86
139
  ### Funding arb direction (CRITICAL — do NOT reverse)
87
140
  ```