openbroker 1.0.39 → 1.0.41

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.
package/SKILL.md CHANGED
@@ -39,10 +39,33 @@ openbroker setup # One-command setup (wallet + config + builder app
39
39
  openbroker approve-builder --check # Check builder fee status (for troubleshooting)
40
40
  ```
41
41
 
42
- The `setup` command handles everything:
43
- 1. Generate new wallet or use existing private key
44
- 2. Save config to `~/.openbroker/.env`
45
- 3. Automatically approve builder fee (required for trading)
42
+ The `setup` command offers three modes:
43
+ 1. **Import existing key** use a private key you already have (master wallet)
44
+ 2. **Generate new wallet** — create a fresh master wallet
45
+ 3. **Generate API wallet** (recommended for agents) — creates a restricted wallet that can trade but cannot withdraw
46
+
47
+ For options 1 and 2, setup saves config and approves the builder fee automatically.
48
+ For option 3 (API wallet), see the API Wallet Setup section below.
49
+
50
+ ### API Wallet Setup (Recommended for Agents)
51
+
52
+ API wallets can place trades on behalf of a master account but **cannot withdraw funds**. This is the safest option for automated agents.
53
+
54
+ **Flow:**
55
+ 1. Run `openbroker setup` and choose option 3 ("Generate API wallet")
56
+ 2. The CLI generates a keypair and prints an approval URL (e.g. `https://openbroker.dev/approve?agent=0xABC...`)
57
+ 3. The agent owner opens the URL in a browser and connects their master wallet (MetaMask etc.)
58
+ 4. The master wallet signs two transactions: `ApproveAgent` (authorizes the API wallet) and `ApproveBuilderFee` (approves the 1 bps fee)
59
+ 5. The CLI detects the approval automatically and saves the config
60
+
61
+ **After setup, the config will contain:**
62
+ ```
63
+ HYPERLIQUID_PRIVATE_KEY=0x... # API wallet private key
64
+ HYPERLIQUID_ACCOUNT_ADDRESS=0x... # Master account address
65
+ HYPERLIQUID_NETWORK=mainnet
66
+ ```
67
+
68
+ **Important for agents:** When using an API wallet, pass the approval URL to the agent owner (the human who controls the master wallet). The owner must approve in a browser before the agent can trade. The CLI waits up to 10 minutes for the approval. If it times out, re-run `openbroker setup`.
46
69
 
47
70
  ### Account Info
48
71
  ```bash
@@ -294,7 +317,9 @@ Run `openbroker setup` to create the global config interactively.
294
317
  |----------|----------|-------------|
295
318
  | `HYPERLIQUID_PRIVATE_KEY` | Yes | Wallet private key (0x...) |
296
319
  | `HYPERLIQUID_NETWORK` | No | `mainnet` (default) or `testnet` |
297
- | `HYPERLIQUID_ACCOUNT_ADDRESS` | No | For API wallets |
320
+ | `HYPERLIQUID_ACCOUNT_ADDRESS` | No | Master account address (required for API wallets) |
321
+
322
+ The builder fee (1 bps / 0.01%) is hardcoded and not configurable.
298
323
 
299
324
  ## Risk Warning
300
325
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openbroker",
3
- "version": "1.0.39",
3
+ "version": "1.0.41",
4
4
  "description": "Hyperliquid trading CLI - execute orders, manage positions, and run trading strategies",
5
5
  "type": "module",
6
6
  "bin": {
@@ -109,9 +109,9 @@ export function loadConfig(): OpenBrokerConfig {
109
109
  const network = process.env.HYPERLIQUID_NETWORK || 'mainnet';
110
110
  const baseUrl = network === 'testnet' ? TESTNET_URL : MAINNET_URL;
111
111
 
112
- // Use open-broker address by default, but allow override for custom builders
113
- const builderAddress = (process.env.BUILDER_ADDRESS || OPEN_BROKER_BUILDER_ADDRESS).toLowerCase();
114
- const builderFee = parseInt(process.env.BUILDER_FEE || '10', 10); // default 1 bps
112
+ // Builder fee is hardcoded — set by OpenBroker, not configurable by users
113
+ const builderAddress = OPEN_BROKER_BUILDER_ADDRESS.toLowerCase();
114
+ const builderFee = 10; // 10 tenths-of-bps = 1 bps = 0.01%
115
115
  const slippageBps = parseInt(process.env.SLIPPAGE_BPS || '50', 10); // default 0.5%
116
116
 
117
117
  // Derive the wallet address from private key
@@ -9,6 +9,7 @@ import * as readline from 'readline';
9
9
  import { homedir } from 'os';
10
10
 
11
11
  const OPEN_BROKER_BUILDER_ADDRESS = '0xbb67021fA3e62ab4DA985bb5a55c5c1884381068';
12
+ const OPENBROKER_URL = process.env.OPENBROKER_URL || 'https://openbroker.dev';
12
13
 
13
14
  // Global config directory: ~/.openbroker/
14
15
  const CONFIG_DIR = path.join(homedir(), '.openbroker');
@@ -46,6 +47,183 @@ function ensureConfigDir(): void {
46
47
  }
47
48
  }
48
49
 
50
+ // ── Polling & verification helpers ──
51
+
52
+ const POLL_INTERVAL_MS = 3000;
53
+ const POLL_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
54
+
55
+ async function pollForApproval(agentAddress: string): Promise<string | null> {
56
+ const startTime = Date.now();
57
+ const statusUrl = `${OPENBROKER_URL}/api/approve-status?agent=${agentAddress}`;
58
+
59
+ let dotCount = 0;
60
+
61
+ while (Date.now() - startTime < POLL_TIMEOUT_MS) {
62
+ try {
63
+ const response = await fetch(statusUrl);
64
+ const data = await response.json() as { status: string; master?: string };
65
+
66
+ if (data.status === 'approved' && data.master) {
67
+ process.stdout.write('\r' + ' '.repeat(50) + '\r');
68
+ return data.master;
69
+ }
70
+
71
+ if (data.status === 'expired') {
72
+ return null;
73
+ }
74
+ } catch {
75
+ // Network error — keep polling
76
+ }
77
+
78
+ dotCount = (dotCount + 1) % 4;
79
+ process.stdout.write(`\r Waiting for browser approval${'.'.repeat(dotCount)}${' '.repeat(3 - dotCount)}`);
80
+
81
+ await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL_MS));
82
+ }
83
+
84
+ process.stdout.write('\r' + ' '.repeat(50) + '\r');
85
+ return null;
86
+ }
87
+
88
+ async function verifyBuilderFee(masterAddress: string): Promise<boolean> {
89
+ try {
90
+ const response = await fetch('https://api.hyperliquid.xyz/info', {
91
+ method: 'POST',
92
+ headers: { 'Content-Type': 'application/json' },
93
+ body: JSON.stringify({
94
+ type: 'maxBuilderFee',
95
+ user: masterAddress.toLowerCase(),
96
+ builder: OPEN_BROKER_BUILDER_ADDRESS.toLowerCase(),
97
+ }),
98
+ });
99
+ const data = await response.json();
100
+ return data !== null && data !== 0 && data !== '0';
101
+ } catch {
102
+ return false;
103
+ }
104
+ }
105
+
106
+ function buildApiWalletEnvContent(privateKey: string, masterAddress: string): string {
107
+ return `# OpenBroker Configuration (API Wallet)
108
+ # Location: ~/.openbroker/.env
109
+ # WARNING: Keep this file secret! Never share it!
110
+
111
+ # API wallet private key (can trade, cannot withdraw)
112
+ HYPERLIQUID_PRIVATE_KEY=${privateKey}
113
+
114
+ # Master account address (the wallet that owns the funds)
115
+ HYPERLIQUID_ACCOUNT_ADDRESS=${masterAddress}
116
+
117
+ # Network: mainnet or testnet
118
+ HYPERLIQUID_NETWORK=mainnet
119
+ `;
120
+ }
121
+
122
+ // ── API wallet setup flow ──
123
+
124
+ async function setupApiWallet(): Promise<OnboardResult> {
125
+ console.log('\nGenerating API wallet keypair...');
126
+ const privateKey = generatePrivateKey();
127
+ const apiAccount = privateKeyToAccount(privateKey);
128
+ console.log(`✅ API Wallet Address: ${apiAccount.address}\n`);
129
+
130
+ // Save partial config immediately (so the key isn't lost)
131
+ console.log('Step 2/3: Creating config...');
132
+ ensureConfigDir();
133
+
134
+ // Build the approval URL
135
+ const approveUrl = `${OPENBROKER_URL}/approve?agent=${apiAccount.address}`;
136
+
137
+ console.log(`✅ Config directory ready: ${CONFIG_DIR}\n`);
138
+
139
+ console.log('Step 3/3: Master wallet approval');
140
+ console.log('================================\n');
141
+ console.log('Your API wallet needs to be authorized by a master wallet.');
142
+ console.log('Open this URL in your browser and connect your master wallet:\n');
143
+ console.log(` ${approveUrl}\n`);
144
+ console.log('The master wallet will sign two transactions:');
145
+ console.log(' 1. ApproveAgent — authorizes this API wallet to trade');
146
+ console.log(' 2. ApproveBuilderFee — approves the 1 bps builder fee\n');
147
+
148
+ // Poll for approval
149
+ const masterAddress = await pollForApproval(apiAccount.address);
150
+
151
+ if (!masterAddress) {
152
+ console.log('\n⚠️ Approval timed out or was not completed.');
153
+ console.log(` You can retry by visiting: ${approveUrl}`);
154
+ console.log(' After approval, re-run: openbroker setup\n');
155
+
156
+ // Save config without master address so user can manually add it later
157
+ const partialEnv = `# OpenBroker Configuration (API Wallet — INCOMPLETE)
158
+ # Location: ~/.openbroker/.env
159
+ # WARNING: Keep this file secret! Never share it!
160
+ # NOTE: Approval not completed. Re-run "openbroker setup" after approving.
161
+
162
+ # API wallet private key
163
+ HYPERLIQUID_PRIVATE_KEY=${privateKey}
164
+
165
+ # TODO: Set this after approving at ${approveUrl}
166
+ # HYPERLIQUID_ACCOUNT_ADDRESS=0x...
167
+
168
+ HYPERLIQUID_NETWORK=mainnet
169
+ `;
170
+ fs.writeFileSync(CONFIG_PATH, partialEnv, { mode: 0o600 });
171
+ console.log(` Partial config saved to: ${CONFIG_PATH}`);
172
+
173
+ return { success: false, error: 'Approval not completed' };
174
+ }
175
+
176
+ console.log(`\n✅ Master wallet detected: ${masterAddress}`);
177
+
178
+ // Verify on-chain
179
+ console.log(' Verifying builder fee approval...');
180
+ const feeApproved = await verifyBuilderFee(masterAddress);
181
+
182
+ if (feeApproved) {
183
+ console.log(' ✅ Builder fee: approved on-chain');
184
+ } else {
185
+ console.log(' ⚠️ Builder fee not yet confirmed on-chain (may take a moment)');
186
+ }
187
+
188
+ // Save complete config
189
+ const envContent = buildApiWalletEnvContent(privateKey, masterAddress);
190
+ fs.writeFileSync(CONFIG_PATH, envContent, { mode: 0o600 });
191
+ console.log(`\n✅ Config saved to: ${CONFIG_PATH}`);
192
+
193
+ // Final summary
194
+ console.log('\n========================================');
195
+ console.log(' SETUP COMPLETE! ');
196
+ console.log('========================================\n');
197
+
198
+ console.log('API Wallet Setup');
199
+ console.log('-----------------');
200
+ console.log(`API Wallet: ${apiAccount.address}`);
201
+ console.log(`Master Account: ${masterAddress}`);
202
+ console.log(`Network: Hyperliquid (Mainnet)`);
203
+ console.log(`Config: ${CONFIG_PATH}`);
204
+
205
+ console.log('\n📋 Next Steps');
206
+ console.log('--------------');
207
+ console.log('1. Ensure your master wallet is funded on Hyperliquid');
208
+ console.log('2. Start trading:');
209
+ console.log(' openbroker account');
210
+ console.log(' openbroker buy --coin ETH --size 0.01 --dry');
211
+
212
+ console.log('\n⚠️ Security');
213
+ console.log('------------');
214
+ console.log('This API wallet can trade but CANNOT withdraw funds.');
215
+ console.log('You can revoke access at any time from app.hyperliquid.xyz');
216
+ console.log(`Config stored at: ${CONFIG_PATH}`);
217
+
218
+ return {
219
+ success: true,
220
+ walletAddress: apiAccount.address,
221
+ privateKey: privateKey,
222
+ };
223
+ }
224
+
225
+ // ── Main ──
226
+
49
227
  async function main(): Promise<OnboardResult> {
50
228
  console.log('OpenBroker - One-Command Setup');
51
229
  console.log('==============================\n');
@@ -84,32 +262,44 @@ async function main(): Promise<OnboardResult> {
84
262
  };
85
263
  }
86
264
 
87
- // Ask user if they have an existing private key
265
+ // Ask user which setup mode
88
266
  const rl = createReadline();
89
267
 
90
268
  console.log('Step 1/3: Wallet Setup');
91
269
  console.log('----------------------');
92
- console.log('Do you have an existing Hyperliquid private key?\n');
93
- console.log(' 1) Yes, I have a private key ready');
94
- console.log(' 2) No, generate a new wallet for me\n');
270
+ console.log('How would you like to set up your wallet?\n');
271
+ console.log(' 1) Import existing private key (master wallet)');
272
+ console.log(' 2) Generate a new wallet (master wallet)');
273
+ console.log(' 3) Generate API wallet (recommended for agents)');
274
+ console.log(' Safer: can trade but cannot withdraw funds.');
275
+ console.log(' Requires browser approval from your master wallet.\n');
95
276
 
96
277
  let choice = '';
97
- while (choice !== '1' && choice !== '2') {
98
- choice = await prompt(rl, 'Enter choice (1 or 2): ');
99
- if (choice !== '1' && choice !== '2') {
100
- console.log('Please enter 1 or 2');
278
+ while (choice !== '1' && choice !== '2' && choice !== '3') {
279
+ choice = await prompt(rl, 'Enter choice (1, 2, or 3): ');
280
+ if (choice !== '1' && choice !== '2' && choice !== '3') {
281
+ console.log('Please enter 1, 2, or 3');
101
282
  }
102
283
  }
103
284
 
285
+ rl.close();
286
+
287
+ // Option 3: API wallet flow
288
+ if (choice === '3') {
289
+ return setupApiWallet();
290
+ }
291
+
292
+ // Options 1 & 2: Master wallet flow
104
293
  let privateKey: `0x${string}`;
105
294
 
106
295
  if (choice === '1') {
107
296
  // User has existing key
297
+ const rl2 = createReadline();
108
298
  console.log('\nEnter your private key (0x... format):\n');
109
299
 
110
300
  let validKey = false;
111
301
  while (!validKey) {
112
- const inputKey = await prompt(rl, 'Private key: ');
302
+ const inputKey = await prompt(rl2, 'Private key: ');
113
303
 
114
304
  if (isValidPrivateKey(inputKey)) {
115
305
  privateKey = inputKey as `0x${string}`;
@@ -119,6 +309,7 @@ async function main(): Promise<OnboardResult> {
119
309
  console.log('Example: 0x1234...abcd (66 characters total)\n');
120
310
  }
121
311
  }
312
+ rl2.close();
122
313
 
123
314
  console.log('\n✅ Private key accepted');
124
315
  } else {
@@ -128,8 +319,6 @@ async function main(): Promise<OnboardResult> {
128
319
  console.log('✅ New wallet created');
129
320
  }
130
321
 
131
- rl.close();
132
-
133
322
  // Derive account from private key
134
323
  const account = privateKeyToAccount(privateKey);
135
324
  console.log(`\nWallet Address: ${account.address}\n`);
@@ -147,11 +336,6 @@ HYPERLIQUID_PRIVATE_KEY=${privateKey}
147
336
 
148
337
  # Network: mainnet or testnet
149
338
  HYPERLIQUID_NETWORK=mainnet
150
-
151
- # Builder fee (supports openbroker development)
152
- # Default: 1 bps (0.01%) on trades
153
- BUILDER_ADDRESS=${OPEN_BROKER_BUILDER_ADDRESS}
154
- BUILDER_FEE=10
155
339
  `;
156
340
 
157
341
  fs.writeFileSync(CONFIG_PATH, envContent, { mode: 0o600 });