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 +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/core/client.ts +28 -6
- package/scripts/info/account.ts +7 -0
- package/scripts/info/positions.ts +4 -0
- package/scripts/plugin/tools.ts +5 -0
- package/scripts/setup/onboard.ts +71 -20
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.
|
|
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
|
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
package/scripts/core/client.ts
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
1161
|
-
summary.totalNtlPos =
|
|
1162
|
-
summary.totalRawUsd =
|
|
1163
|
-
summary.totalMarginUsed =
|
|
1164
|
-
summary.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 {
|
package/scripts/info/account.ts
CHANGED
|
@@ -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
|
|
package/scripts/plugin/tools.ts
CHANGED
|
@@ -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
|
},
|
package/scripts/setup/onboard.ts
CHANGED
|
@@ -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:
|
|
255
|
-
|
|
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:
|
|
261
|
-
|
|
311
|
+
success: true,
|
|
312
|
+
walletAddress: account.address,
|
|
262
313
|
};
|
|
263
314
|
}
|
|
264
315
|
|