openbroker 1.0.69 → 1.0.70

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
@@ -4,7 +4,7 @@ description: Hyperliquid trading plugin with background position monitoring and
4
4
  license: MIT
5
5
  compatibility: Requires Node.js 22+, network access to api.hyperliquid.xyz
6
6
  homepage: https://www.npmjs.com/package/openbroker
7
- metadata: {"author": "monemetrics", "version": "1.0.69", "openclaw": {"requires": {"bins": ["openbroker"], "env": ["HYPERLIQUID_PRIVATE_KEY"]}, "primaryEnv": "HYPERLIQUID_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "openbroker", "bins": ["openbroker"], "label": "Install openbroker (npm)"}]}}
7
+ metadata: {"author": "monemetrics", "version": "1.0.70", "openclaw": {"requires": {"bins": ["openbroker"], "env": ["HYPERLIQUID_PRIVATE_KEY"]}, "primaryEnv": "HYPERLIQUID_PRIVATE_KEY", "install": [{"id": "node", "kind": "node", "package": "openbroker", "bins": ["openbroker"], "label": "Install openbroker (npm)"}]}}
8
8
  allowed-tools: ob_account ob_positions ob_funding ob_markets ob_search ob_spot ob_fills ob_orders ob_order_status ob_fees ob_candles ob_funding_history ob_trades ob_rate_limit ob_funding_scan ob_buy ob_sell ob_limit ob_trigger ob_tpsl ob_cancel ob_twap ob_bracket ob_chase ob_watcher_status ob_auto_run ob_auto_stop ob_auto_list Bash(openbroker:*)
9
9
  ---
10
10
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openbroker",
3
3
  "name": "OpenBroker — Hyperliquid Trading",
4
- "version": "1.0.69",
4
+ "version": "1.0.70",
5
5
  "description": "Trade on Hyperliquid DEX with background position monitoring",
6
6
  "configSchema": {
7
7
  "type": "object",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openbroker",
3
- "version": "1.0.69",
3
+ "version": "1.0.70",
4
4
  "description": "Hyperliquid trading CLI - execute orders, manage positions, and run trading strategies",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1126,7 +1126,18 @@ export class HyperliquidClient {
1126
1126
  const params: { user: string; dex?: string } = { user: user ?? this.address };
1127
1127
  if (dex !== undefined) params.dex = dex;
1128
1128
  const response = await this.info.clearinghouseState(params as any);
1129
- return response as ClearinghouseState;
1129
+
1130
+ // The SDK response has `withdrawable` as a top-level field, not inside
1131
+ // marginSummary/crossMarginSummary. Copy it into our MarginSummary shape.
1132
+ const state = response as unknown as ClearinghouseState;
1133
+ const withdrawable = (response as any).withdrawable ?? '0';
1134
+ if (state.marginSummary) {
1135
+ state.marginSummary.withdrawable = withdrawable;
1136
+ }
1137
+ if (state.crossMarginSummary) {
1138
+ state.crossMarginSummary.withdrawable = withdrawable;
1139
+ }
1140
+ return state;
1130
1141
  }
1131
1142
 
1132
1143
  /**
@@ -1142,6 +1153,7 @@ export class HyperliquidClient {
1142
1153
  const dexs = await this.getPerpDexs();
1143
1154
 
1144
1155
  // Collect positions from all HIP-3 dexes
1156
+ let hip3Errors = 0;
1145
1157
  for (let i = 1; i < dexs.length; i++) {
1146
1158
  const dex = dexs[i];
1147
1159
  if (!dex) continue;
@@ -1156,22 +1168,32 @@ export class HyperliquidClient {
1156
1168
  if (!unified) {
1157
1169
  const dexMargin = dexState.marginSummary;
1158
1170
  if (dexMargin) {
1171
+ const safeAdd = (a: string | undefined, b: string | undefined): string => {
1172
+ const va = parseFloat(a ?? '0') || 0;
1173
+ const vb = parseFloat(b ?? '0') || 0;
1174
+ return String(va + vb);
1175
+ };
1159
1176
  const addToSummary = (summary: { accountValue: string; totalNtlPos: string; totalRawUsd: string; totalMarginUsed: string; withdrawable: string }) => {
1160
- summary.accountValue = String(parseFloat(summary.accountValue) + parseFloat(dexMargin.accountValue));
1161
- summary.totalNtlPos = String(parseFloat(summary.totalNtlPos) + parseFloat(dexMargin.totalNtlPos));
1162
- summary.totalRawUsd = String(parseFloat(summary.totalRawUsd) + parseFloat(dexMargin.totalRawUsd));
1163
- summary.totalMarginUsed = String(parseFloat(summary.totalMarginUsed) + parseFloat(dexMargin.totalMarginUsed));
1164
- summary.withdrawable = String(parseFloat(summary.withdrawable) + parseFloat(dexMargin.withdrawable));
1177
+ summary.accountValue = safeAdd(summary.accountValue, dexMargin.accountValue);
1178
+ summary.totalNtlPos = safeAdd(summary.totalNtlPos, dexMargin.totalNtlPos);
1179
+ summary.totalRawUsd = safeAdd(summary.totalRawUsd, dexMargin.totalRawUsd);
1180
+ summary.totalMarginUsed = safeAdd(summary.totalMarginUsed, dexMargin.totalMarginUsed);
1181
+ summary.withdrawable = safeAdd(summary.withdrawable, dexMargin.withdrawable);
1165
1182
  };
1166
1183
  addToSummary(mainState.marginSummary);
1167
1184
  addToSummary(mainState.crossMarginSummary);
1168
1185
  }
1169
1186
  }
1170
1187
  } catch (err) {
1188
+ hip3Errors++;
1171
1189
  this.log(`Failed to fetch state for dex ${dex.name}:`, err instanceof Error ? err.message : String(err));
1172
1190
  }
1173
1191
  }
1174
1192
 
1193
+ if (hip3Errors > 0) {
1194
+ this.log(`Warning: ${hip3Errors} HIP-3 dex queries failed — some positions may be missing. Use --verbose for details.`);
1195
+ }
1196
+
1175
1197
  // For unified accounts: equity is the USDC balance from spot clearinghouse
1176
1198
  if (unified) {
1177
1199
  try {
@@ -109,6 +109,13 @@ async function main() {
109
109
  console.log(`Builder Approved: ❌ No`);
110
110
  console.log(`\n⚠️ Run: npx tsx scripts/setup/approve-builder.ts`);
111
111
  }
112
+
113
+ // Warn if API wallet setup looks misconfigured
114
+ if (!client.isApiWallet && accountValue === 0 && positions.length === 0) {
115
+ console.log('\n⚠️ No positions and $0 equity.');
116
+ console.log(' If this account is traded via an API wallet, set HYPERLIQUID_ACCOUNT_ADDRESS');
117
+ console.log(' in ~/.openbroker/.env to the master account address (the wallet that holds funds).');
118
+ }
112
119
  console.log('');
113
120
 
114
121
  console.log('Margin Summary');
@@ -71,6 +71,10 @@ async function main() {
71
71
 
72
72
  if (positions.length === 0) {
73
73
  console.log(filterCoin ? `No position in ${filterCoin}` : 'No open positions');
74
+ if (!filterCoin && !client.isApiWallet) {
75
+ console.log('\n⚠️ If this account is traded via an API wallet, set HYPERLIQUID_ACCOUNT_ADDRESS');
76
+ console.log(' in ~/.openbroker/.env to the master account address.');
77
+ }
74
78
  return;
75
79
  }
76
80
 
@@ -89,6 +89,11 @@ export function createTools(watcherOrCtx: PositionWatcher | null | ToolsContext)
89
89
  }));
90
90
  }
91
91
 
92
+ // Warn if likely misconfigured API wallet (querying the API wallet address instead of master)
93
+ if (!client.isApiWallet && accountValue === 0 && state.assetPositions.filter(ap => parseFloat(ap.position.szi) !== 0).length === 0) {
94
+ result.warning = 'No positions and $0 equity. If using an API wallet, ensure HYPERLIQUID_ACCOUNT_ADDRESS is set to the master account address in ~/.openbroker/.env or plugin config.';
95
+ }
96
+
92
97
  return json(result);
93
98
  },
94
99
  },
@@ -231,34 +231,85 @@ async function main(): Promise<OnboardResult> {
231
231
 
232
232
  // Check if config already exists
233
233
  if (fs.existsSync(CONFIG_PATH)) {
234
- console.log('⚠️ Config already exists!');
235
- console.log(` Location: ${CONFIG_PATH}\n`);
236
-
237
- // Read existing config and show wallet address
238
234
  const envContent = fs.readFileSync(CONFIG_PATH, 'utf-8');
239
235
  const keyMatch = envContent.match(/HYPERLIQUID_PRIVATE_KEY=0x([a-fA-F0-9]{64})/);
240
236
 
241
- if (keyMatch) {
242
- const existingKey = `0x${keyMatch[1]}` as `0x${string}`;
243
- const account = privateKeyToAccount(existingKey);
244
- console.log('Current Configuration');
245
- console.log('---------------------');
246
- console.log(`Wallet Address: ${account.address}`);
247
- console.log(`Config File: ${CONFIG_PATH}`);
248
- console.log(`\nTo reconfigure, delete the config file first:`);
249
- console.log(` rm ${CONFIG_PATH}`);
250
- console.log(`\nTo fund this wallet, send USDC on Arbitrum, then deposit at:`);
251
- console.log(` https://app.hyperliquid.xyz/`);
252
-
237
+ if (!keyMatch) {
253
238
  return {
254
- success: true,
255
- walletAddress: account.address,
239
+ success: false,
240
+ error: 'Invalid config file - missing or malformed private key',
256
241
  };
257
242
  }
258
243
 
244
+ const existingKey = `0x${keyMatch[1]}` as `0x${string}`;
245
+ const account = privateKeyToAccount(existingKey);
246
+
247
+ // Check if this is an incomplete API wallet setup (HYPERLIQUID_ACCOUNT_ADDRESS missing or commented out)
248
+ const hasAccountAddress = /^HYPERLIQUID_ACCOUNT_ADDRESS=0x[a-fA-F0-9]{40}/m.test(envContent);
249
+ const isIncompleteApiWallet = envContent.includes('INCOMPLETE') || envContent.includes('# HYPERLIQUID_ACCOUNT_ADDRESS');
250
+
251
+ if (!hasAccountAddress && isIncompleteApiWallet) {
252
+ console.log('⚠️ Incomplete API wallet setup detected!');
253
+ console.log(` API Wallet: ${account.address}`);
254
+ console.log(` Master account address is missing — re-polling for approval...\n`);
255
+
256
+ const approveUrl = `${OPENBROKER_URL}/approve?agent=${account.address}`;
257
+ console.log(` If not yet approved, visit: ${approveUrl}\n`);
258
+
259
+ const masterAddress = await pollForApproval(account.address);
260
+
261
+ if (masterAddress) {
262
+ console.log(`\n✅ Master wallet detected: ${masterAddress}`);
263
+
264
+ // Verify builder fee on-chain
265
+ console.log(' Verifying builder fee approval...');
266
+ const feeApproved = await verifyBuilderFee(masterAddress);
267
+ if (feeApproved) {
268
+ console.log(' ✅ Builder fee: approved on-chain');
269
+ } else {
270
+ console.log(' ⚠️ Builder fee not yet confirmed on-chain (may take a moment)');
271
+ }
272
+
273
+ // Save complete config
274
+ const completeEnv = buildApiWalletEnvContent(existingKey, masterAddress);
275
+ fs.writeFileSync(CONFIG_PATH, completeEnv, { mode: 0o600 });
276
+
277
+ console.log(`\n✅ Config updated: ${CONFIG_PATH}`);
278
+ console.log(` API Wallet: ${account.address}`);
279
+ console.log(` Master Account: ${masterAddress}`);
280
+ console.log('\n Start trading: openbroker account');
281
+
282
+ return { success: true, walletAddress: account.address };
283
+ }
284
+
285
+ console.log('\n⚠️ Approval still not completed.');
286
+ console.log(` Visit: ${approveUrl}`);
287
+ console.log(' Then re-run: openbroker setup\n');
288
+ return { success: false, error: 'Approval not completed' };
289
+ }
290
+
291
+ // Config exists and is complete
292
+ console.log('⚠️ Config already exists!');
293
+ console.log(` Location: ${CONFIG_PATH}\n`);
294
+ console.log('Current Configuration');
295
+ console.log('---------------------');
296
+ console.log(`Wallet Address: ${account.address}`);
297
+ if (hasAccountAddress) {
298
+ const addrMatch = envContent.match(/HYPERLIQUID_ACCOUNT_ADDRESS=(0x[a-fA-F0-9]+)/);
299
+ if (addrMatch) {
300
+ console.log(`Master Account: ${addrMatch[1]}`);
301
+ console.log(`Wallet Type: API Wallet`);
302
+ }
303
+ }
304
+ console.log(`Config File: ${CONFIG_PATH}`);
305
+ console.log(`\nTo reconfigure, delete the config file first:`);
306
+ console.log(` rm ${CONFIG_PATH}`);
307
+ console.log(`\nTo fund this wallet, send USDC on Arbitrum, then deposit at:`);
308
+ console.log(` https://app.hyperliquid.xyz/`);
309
+
259
310
  return {
260
- success: false,
261
- error: 'Invalid config file - missing or malformed private key',
311
+ success: true,
312
+ walletAddress: account.address,
262
313
  };
263
314
  }
264
315