openbroker 1.0.80 → 1.0.85

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.
@@ -7,6 +7,7 @@ interface Args {
7
7
  query: string;
8
8
  type?: 'perp' | 'spot' | 'hip3' | 'all';
9
9
  verbose?: boolean;
10
+ json?: boolean;
10
11
  }
11
12
 
12
13
  function parseArgs(): Args {
@@ -23,6 +24,8 @@ function parseArgs(): Args {
23
24
  }
24
25
  } else if (arg === '--verbose') {
25
26
  args.verbose = true;
27
+ } else if (arg === '--json') {
28
+ args.json = true;
26
29
  } else if (arg === '--help' || arg === '-h') {
27
30
  console.log(`
28
31
  Search Markets - Find assets across all Hyperliquid markets
@@ -32,6 +35,7 @@ Usage: npx tsx scripts/info/search-markets.ts --query <search> [options]
32
35
  Options:
33
36
  --query <search> Search term (required) - matches coin name
34
37
  --type <type> Filter by market type: perp, spot, hip3, or all (default: all)
38
+ --json Output as JSON (machine-readable)
35
39
  --verbose Show detailed output
36
40
  --help Show this help
37
41
 
@@ -40,6 +44,7 @@ Examples:
40
44
  npx tsx scripts/info/search-markets.ts --query BTC # Find all BTC markets
41
45
  npx tsx scripts/info/search-markets.ts --query ETH --type perp # ETH perps only
42
46
  npx tsx scripts/info/search-markets.ts --query PURR --type spot # PURR spot only
47
+ npx tsx scripts/info/search-markets.ts --query HYPE --json # JSON output
43
48
  `);
44
49
  process.exit(0);
45
50
  } else if (!args.query && !arg.startsWith('-')) {
@@ -85,18 +90,23 @@ async function main() {
85
90
  client.verbose = args.verbose ?? false;
86
91
 
87
92
  const query = args.query.toUpperCase();
88
- console.log(`Searching for "${query}" across all markets...\n`);
93
+ if (!args.json) {
94
+ console.log(`Searching for "${query}" across all markets...\n`);
95
+ }
89
96
 
90
- const results: Array<{
97
+ interface Result {
91
98
  type: 'perp' | 'spot' | 'hip3';
92
99
  provider: string;
93
100
  coin: string;
101
+ assetId: number;
94
102
  price: string;
95
103
  volume24h: number;
96
104
  funding?: string;
97
105
  maxLeverage?: number;
98
106
  openInterest?: string;
99
- }> = [];
107
+ }
108
+
109
+ const results: Result[] = [];
100
110
 
101
111
  // Search main perps
102
112
  if (args.type === 'all' || args.type === 'perp') {
@@ -110,6 +120,7 @@ async function main() {
110
120
  type: 'perp',
111
121
  provider: 'Hyperliquid',
112
122
  coin: asset.name,
123
+ assetId: i,
113
124
  price: ctx.markPx,
114
125
  volume24h: parseFloat(ctx.dayNtlVlm),
115
126
  funding: ctx.funding,
@@ -122,6 +133,14 @@ async function main() {
122
133
 
123
134
  // Search HIP-3 perps
124
135
  if (args.type === 'all' || args.type === 'hip3') {
136
+ if (client.isTestnet) {
137
+ // On testnet, load specific dex if query is "dex:COIN" format
138
+ if (args.query.includes(':')) {
139
+ await client.loadSingleHip3Dex(args.query.split(':')[0]);
140
+ } else if (!args.json) {
141
+ console.log(' (Testnet: HIP-3 dexes not auto-loaded. Use "dexName:COIN" to search a specific dex.)\n');
142
+ }
143
+ }
125
144
  try {
126
145
  const allPerpMetas = await client.getAllPerpMetas();
127
146
  // Skip index 0 (main dex), process HIP-3 dexs
@@ -135,10 +154,13 @@ async function main() {
135
154
  if (!asset || !ctx) continue;
136
155
 
137
156
  if (asset.name.toUpperCase().includes(query)) {
157
+ let assetId = -1;
158
+ try { assetId = client.getAssetIndex(asset.name); } catch { /* not registered */ }
138
159
  results.push({
139
160
  type: 'hip3',
140
161
  provider: dexData.dexName || `HIP-3 DEX ${dexIdx}`,
141
162
  coin: asset.name,
163
+ assetId,
142
164
  price: ctx.markPx,
143
165
  volume24h: parseFloat(ctx.dayNtlVlm || '0'),
144
166
  funding: ctx.funding,
@@ -191,6 +213,7 @@ async function main() {
191
213
  type: 'spot',
192
214
  provider: 'Spot',
193
215
  coin: displayName,
216
+ assetId: 10000 + pair.index,
194
217
  price: ctx.markPx,
195
218
  volume24h: parseFloat(ctx.dayNtlVlm || '0'),
196
219
  });
@@ -204,20 +227,25 @@ async function main() {
204
227
  // Sort by volume
205
228
  results.sort((a, b) => b.volume24h - a.volume24h);
206
229
 
230
+ if (args.json) {
231
+ console.log(JSON.stringify(results, null, 2));
232
+ return;
233
+ }
234
+
207
235
  if (results.length === 0) {
208
236
  console.log(`No markets found matching "${query}"`);
209
237
  return;
210
238
  }
211
239
 
212
240
  console.log(`Found ${results.length} market(s) matching "${query}":\n`);
213
- console.log('Type Provider Coin Price 24h Volume Funding (Ann.) OI');
214
- console.log('-'.repeat(100));
241
+ console.log('Type Provider Coin AssetID Price 24h Volume Funding (Ann.) OI');
242
+ console.log('-'.repeat(112));
215
243
 
216
244
  for (const m of results) {
217
245
  const typeStr = m.type === 'hip3' ? 'HIP-3' : m.type.charAt(0).toUpperCase() + m.type.slice(1);
218
246
  const oi = m.openInterest ? formatVolume(parseFloat(m.openInterest)) : '-';
219
247
  console.log(
220
- `${typeStr.padEnd(8)} ${m.provider.padEnd(16)} ${m.coin.padEnd(14)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)} ${(m.funding ? formatFunding(m.funding) : '-').padStart(14)} ${oi.padStart(10)}`
248
+ `${typeStr.padEnd(8)} ${m.provider.padEnd(16)} ${m.coin.padEnd(14)} ${String(m.assetId).padStart(7)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)} ${(m.funding ? formatFunding(m.funding) : '-').padStart(14)} ${oi.padStart(10)}`
221
249
  );
222
250
  }
223
251
 
@@ -237,11 +265,11 @@ async function main() {
237
265
  const perpsWithFunding = markets.filter((m) => m.funding && m.type !== 'spot');
238
266
  if (perpsWithFunding.length > 1) {
239
267
  console.log(`\n=== ${coin} Funding Comparison ===\n`);
240
- console.log('Provider Coin Funding (Ann.) Price');
241
- console.log('-'.repeat(65));
268
+ console.log('Provider Coin AssetID Funding (Ann.) Price');
269
+ console.log('-'.repeat(78));
242
270
  for (const m of perpsWithFunding.sort((a, b) => parseFloat(b.funding!) - parseFloat(a.funding!))) {
243
271
  console.log(
244
- `${m.provider.padEnd(16)} ${m.coin.padEnd(14)} ${formatFunding(m.funding!).padStart(14)} ${formatPrice(m.price)}`
272
+ `${m.provider.padEnd(16)} ${m.coin.padEnd(14)} ${String(m.assetId).padStart(7)} ${formatFunding(m.funding!).padStart(14)} ${formatPrice(m.price)}`
245
273
  );
246
274
  }
247
275
  }
@@ -9,6 +9,7 @@ interface Args {
9
9
  top?: number;
10
10
  verbose?: boolean;
11
11
  address?: string;
12
+ json?: boolean;
12
13
  }
13
14
 
14
15
  function parseArgs(): Args {
@@ -26,6 +27,8 @@ function parseArgs(): Args {
26
27
  args.address = process.argv[++i].toLowerCase();
27
28
  } else if (arg === '--verbose') {
28
29
  args.verbose = true;
30
+ } else if (arg === '--json') {
31
+ args.json = true;
29
32
  } else if (arg === '--help' || arg === '-h') {
30
33
  console.log(`
31
34
  Spot Markets - View Hyperliquid spot markets and balances
@@ -37,6 +40,7 @@ Options:
37
40
  --balances Show your spot token balances
38
41
  --address <0x...> Look up another account's spot balances (with --balances)
39
42
  --top <n> Show only top N markets by volume
43
+ --json Output as JSON (machine-readable)
40
44
  --verbose Show detailed output
41
45
  --help Show this help
42
46
 
@@ -85,10 +89,15 @@ async function main() {
85
89
  // Show balances
86
90
  if (args.balances) {
87
91
  const lookupAddress = args.address ?? client.address;
88
- console.log(`Fetching spot balances for ${lookupAddress}...\n`);
92
+ if (!args.json) console.log(`Fetching spot balances for ${lookupAddress}...\n`);
89
93
 
90
94
  const balances = await client.getSpotBalances(args.address);
91
95
 
96
+ if (args.json) {
97
+ console.log(JSON.stringify(balances.balances ?? [], null, 2));
98
+ return;
99
+ }
100
+
92
101
  if (!balances.balances || balances.balances.length === 0) {
93
102
  console.log('No spot token balances found.');
94
103
  return;
@@ -112,17 +121,20 @@ async function main() {
112
121
  }
113
122
 
114
123
  // Show markets
115
- console.log('Fetching spot market data...\n');
124
+ if (!args.json) console.log('Fetching spot market data...\n');
116
125
 
117
126
  const spotData = await client.getSpotMetaAndAssetCtxs();
118
127
 
119
128
  interface SpotMarket {
120
129
  name: string;
121
130
  index: number;
131
+ assetId: number;
122
132
  price: string;
123
133
  volume24h: number;
124
134
  change24h: string;
125
135
  tokens: [number, number];
136
+ base?: string;
137
+ quote?: string;
126
138
  }
127
139
 
128
140
  const markets: SpotMarket[] = [];
@@ -155,10 +167,13 @@ async function main() {
155
167
  markets.push({
156
168
  name: pair.name,
157
169
  index: pair.index,
170
+ assetId: 10000 + pair.index,
158
171
  price: ctx.markPx,
159
172
  volume24h: parseFloat(ctx.dayNtlVlm || '0'),
160
173
  change24h: formatChange(ctx.markPx, ctx.prevDayPx),
161
174
  tokens: pair.tokens,
175
+ base: tokenNameMap.get(pair.tokens[0]),
176
+ quote: tokenNameMap.get(pair.tokens[1]),
162
177
  });
163
178
  }
164
179
 
@@ -168,6 +183,11 @@ async function main() {
168
183
  // Apply top filter
169
184
  const displayMarkets = args.top ? markets.slice(0, args.top) : markets;
170
185
 
186
+ if (args.json) {
187
+ console.log(JSON.stringify(displayMarkets, null, 2));
188
+ return;
189
+ }
190
+
171
191
  if (displayMarkets.length === 0) {
172
192
  console.log(args.coin ? `No spot markets found for "${args.coin}"` : 'No spot markets found');
173
193
  return;
@@ -180,8 +200,8 @@ async function main() {
180
200
  }
181
201
 
182
202
  console.log(`=== Spot Markets (${displayMarkets.length} total) ===\n`);
183
- console.log('Pair Price 24h Volume 24h Change Base/Quote');
184
- console.log('-'.repeat(80));
203
+ console.log('Pair AssetID Price 24h Volume 24h Change Base/Quote');
204
+ console.log('-'.repeat(92));
185
205
 
186
206
  for (const m of displayMarkets) {
187
207
  const baseToken = tokenMap.get(m.tokens[0]);
@@ -189,7 +209,7 @@ async function main() {
189
209
  const pairStr = `${baseToken?.name || '?'}/${quoteToken?.name || '?'}`;
190
210
 
191
211
  console.log(
192
- `${m.name.padEnd(14)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)} ${m.change24h.padStart(11)} ${pairStr}`
212
+ `${m.name.padEnd(14)} ${String(m.assetId).padStart(7)} ${formatPrice(m.price).padStart(16)} ${formatVolume(m.volume24h).padStart(13)} ${m.change24h.padStart(11)} ${pairStr}`
193
213
  );
194
214
  }
195
215
 
@@ -11,6 +11,7 @@ Usage: openbroker trades --coin <symbol> [options]
11
11
  Options:
12
12
  --coin <symbol> Asset symbol (required, e.g. ETH, BTC)
13
13
  --top <n> Show last N trades (default: 30)
14
+ --json Output as JSON (machine-readable)
14
15
  --help, -h Show this help
15
16
 
16
17
  Examples:
@@ -35,14 +36,20 @@ async function main() {
35
36
  }
36
37
 
37
38
  const top = parseInt(args.top as string) || 30;
39
+ const jsonOutput = args.json as boolean;
38
40
  const client = getClient();
39
41
 
40
- console.log(`Open Broker - ${normalizeCoin(coin)} Recent Trades`);
41
- console.log('='.repeat(40) + '\n');
42
+ if (!jsonOutput) {
43
+ console.log(`Open Broker - ${normalizeCoin(coin)} Recent Trades`);
44
+ console.log('='.repeat(40) + '\n');
45
+ }
42
46
 
43
47
  try {
44
48
  // Load metadata (needed for HIP-3 coin resolution)
45
49
  await client.getMetaAndAssetCtxs();
50
+ if (client.isTestnet && coin.includes(':')) {
51
+ await client.loadSingleHip3Dex(coin.split(':')[0]);
52
+ }
46
53
 
47
54
  let trades = await client.getRecentTrades(normalizeCoin(coin));
48
55
 
@@ -50,6 +57,13 @@ async function main() {
50
57
  trades.sort((a, b) => b.time - a.time);
51
58
  trades = trades.slice(0, top);
52
59
 
60
+ if (jsonOutput) {
61
+ let assetId = -1;
62
+ try { assetId = client.getAssetIndex(normalizeCoin(coin)); } catch { /* noop */ }
63
+ console.log(JSON.stringify({ coin: normalizeCoin(coin), assetId, trades }, null, 2));
64
+ return;
65
+ }
66
+
53
67
  if (trades.length === 0) {
54
68
  console.log('No recent trades found');
55
69
  return;
@@ -12,8 +12,19 @@ const OPEN_BROKER_BUILDER_ADDRESS = '0xbb67021fA3e62ab4DA985bb5a55c5c1884381068'
12
12
  const OPENBROKER_URL = process.env.OPENBROKER_URL || 'https://openbroker.dev';
13
13
 
14
14
  // Global config directory: ~/.openbroker/
15
- const CONFIG_DIR = path.join(homedir(), '.openbroker');
16
- const CONFIG_PATH = path.join(CONFIG_DIR, '.env');
15
+ const GLOBAL_CONFIG_DIR = path.join(homedir(), '.openbroker');
16
+ const GLOBAL_CONFIG_PATH = path.join(GLOBAL_CONFIG_DIR, '.env');
17
+
18
+ // Parse CLI flags
19
+ const cliArgs = process.argv.slice(2);
20
+ const useTestnet = cliArgs.includes('--testnet') || process.env.HYPERLIQUID_NETWORK === 'testnet';
21
+ const accountAddressIdx = cliArgs.indexOf('--account-address');
22
+ const cliAccountAddress = accountAddressIdx !== -1 ? cliArgs[accountAddressIdx + 1] : undefined;
23
+ const configPathIdx = cliArgs.indexOf('-c') !== -1 ? cliArgs.indexOf('-c') : cliArgs.indexOf('--config');
24
+ const cliConfigPath = configPathIdx !== -1 ? cliArgs[configPathIdx + 1] : process.env.OPENBROKER_CONFIG;
25
+
26
+ const CONFIG_PATH = cliConfigPath ? path.resolve(cliConfigPath) : GLOBAL_CONFIG_PATH;
27
+ const CONFIG_DIR = path.dirname(CONFIG_PATH);
17
28
 
18
29
  interface OnboardResult {
19
30
  success: boolean;
@@ -54,7 +65,7 @@ const POLL_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
54
65
 
55
66
  async function pollForApproval(agentAddress: string): Promise<string | null> {
56
67
  const startTime = Date.now();
57
- const statusUrl = `${OPENBROKER_URL}/api/approve-status?agent=${agentAddress}`;
68
+ const statusUrl = `${OPENBROKER_URL}/api/approve-status?agent=${agentAddress}${useTestnet ? '&network=testnet' : ''}`;
58
69
 
59
70
  let dotCount = 0;
60
71
 
@@ -86,8 +97,9 @@ async function pollForApproval(agentAddress: string): Promise<string | null> {
86
97
  }
87
98
 
88
99
  async function verifyBuilderFee(masterAddress: string): Promise<boolean> {
100
+ const apiUrl = useTestnet ? 'https://api.hyperliquid-testnet.xyz/info' : 'https://api.hyperliquid.xyz/info';
89
101
  try {
90
- const response = await fetch('https://api.hyperliquid.xyz/info', {
102
+ const response = await fetch(apiUrl, {
91
103
  method: 'POST',
92
104
  headers: { 'Content-Type': 'application/json' },
93
105
  body: JSON.stringify({
@@ -104,8 +116,9 @@ async function verifyBuilderFee(masterAddress: string): Promise<boolean> {
104
116
  }
105
117
 
106
118
  function buildApiWalletEnvContent(privateKey: string, masterAddress: string): string {
119
+ const network = useTestnet ? 'testnet' : 'mainnet';
107
120
  return `# OpenBroker Configuration (API Wallet)
108
- # Location: ~/.openbroker/.env
121
+ # Location: ${CONFIG_PATH}
109
122
  # WARNING: Keep this file secret! Never share it!
110
123
 
111
124
  # API wallet private key (can trade, cannot withdraw)
@@ -115,7 +128,7 @@ HYPERLIQUID_PRIVATE_KEY=${privateKey}
115
128
  HYPERLIQUID_ACCOUNT_ADDRESS=${masterAddress}
116
129
 
117
130
  # Network: mainnet or testnet
118
- HYPERLIQUID_NETWORK=mainnet
131
+ HYPERLIQUID_NETWORK=${network}
119
132
  `;
120
133
  }
121
134
 
@@ -132,7 +145,7 @@ async function setupApiWallet(): Promise<OnboardResult> {
132
145
  ensureConfigDir();
133
146
 
134
147
  // Build the approval URL
135
- const approveUrl = `${OPENBROKER_URL}/approve?agent=${apiAccount.address}`;
148
+ const approveUrl = `${OPENBROKER_URL}/approve?agent=${apiAccount.address}${useTestnet ? '&network=testnet' : ''}`;
136
149
 
137
150
  console.log(`✅ Config directory ready: ${CONFIG_DIR}\n`);
138
151
 
@@ -141,9 +154,15 @@ async function setupApiWallet(): Promise<OnboardResult> {
141
154
  console.log('Your API wallet needs to be authorized by a master wallet.');
142
155
  console.log('Open this URL in your browser and connect your master wallet:\n');
143
156
  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');
157
+ if (useTestnet) {
158
+ console.log('The master wallet will sign one transaction:');
159
+ console.log(' 1. ApproveAgent authorizes this API wallet to trade');
160
+ console.log(' (Builder fee approval is skipped on testnet)\n');
161
+ } else {
162
+ console.log('The master wallet will sign two transactions:');
163
+ console.log(' 1. ApproveAgent — authorizes this API wallet to trade');
164
+ console.log(' 2. ApproveBuilderFee — approves the 1 bps builder fee\n');
165
+ }
147
166
 
148
167
  // Poll for approval
149
168
  const masterAddress = await pollForApproval(apiAccount.address);
@@ -175,14 +194,18 @@ HYPERLIQUID_NETWORK=mainnet
175
194
 
176
195
  console.log(`\n✅ Master wallet detected: ${masterAddress}`);
177
196
 
178
- // Verify on-chain
179
- console.log(' Verifying builder fee approval...');
180
- const feeApproved = await verifyBuilderFee(masterAddress);
197
+ // Verify builder fee on-chain (skip on testnet)
198
+ if (!useTestnet) {
199
+ console.log(' Verifying builder fee approval...');
200
+ const feeApproved = await verifyBuilderFee(masterAddress);
181
201
 
182
- if (feeApproved) {
183
- console.log(' ✅ Builder fee: approved on-chain');
202
+ if (feeApproved) {
203
+ console.log(' ✅ Builder fee: approved on-chain');
204
+ } else {
205
+ console.log(' ⚠️ Builder fee not yet confirmed on-chain (may take a moment)');
206
+ }
184
207
  } else {
185
- console.log(' ⚠️ Builder fee not yet confirmed on-chain (may take a moment)');
208
+ console.log(' (Builder fee verification skipped on testnet)');
186
209
  }
187
210
 
188
211
  // Save complete config
@@ -225,8 +248,30 @@ HYPERLIQUID_NETWORK=mainnet
225
248
  // ── Main ──
226
249
 
227
250
  async function main(): Promise<OnboardResult> {
251
+ if (cliArgs.includes('--help') || cliArgs.includes('-h')) {
252
+ console.log(`
253
+ OpenBroker Setup
254
+
255
+ Usage: openbroker setup [options]
256
+
257
+ Options:
258
+ -c, --config <path> Save config to a custom path (default: ~/.openbroker/.env)
259
+ --testnet Configure for testnet
260
+ --account-address <addr> Set HYPERLIQUID_ACCOUNT_ADDRESS (for API wallet / vault trading)
261
+ --help Show this help
262
+
263
+ Examples:
264
+ openbroker setup # Interactive → ~/.openbroker/.env
265
+ openbroker setup -c .env --testnet # Write to ./.env for testnet
266
+ openbroker setup -c ./testnet.env --testnet --account-address 0x... # API wallet config
267
+ `);
268
+ process.exit(0);
269
+ }
270
+
228
271
  console.log('OpenBroker - One-Command Setup');
229
272
  console.log('==============================\n');
273
+ if (cliConfigPath) console.log(`Config will be saved to: ${CONFIG_PATH}\n`);
274
+ if (useTestnet) console.log('Network: testnet\n');
230
275
  console.log('This will: 1) Create wallet 2) Save config 3) Approve builder fee\n');
231
276
 
232
277
  // Check if config already exists
@@ -253,7 +298,7 @@ async function main(): Promise<OnboardResult> {
253
298
  console.log(` API Wallet: ${account.address}`);
254
299
  console.log(` Master account address is missing — re-polling for approval...\n`);
255
300
 
256
- const approveUrl = `${OPENBROKER_URL}/approve?agent=${account.address}`;
301
+ const approveUrl = `${OPENBROKER_URL}/approve?agent=${account.address}${useTestnet ? '&network=testnet' : ''}`;
257
302
  console.log(` If not yet approved, visit: ${approveUrl}\n`);
258
303
 
259
304
  const masterAddress = await pollForApproval(account.address);
@@ -380,51 +425,57 @@ async function main(): Promise<OnboardResult> {
380
425
  console.log('Step 2/3: Creating config...');
381
426
  ensureConfigDir();
382
427
 
428
+ const network = useTestnet ? 'testnet' : 'mainnet';
429
+ const accountLine = cliAccountAddress ? `\n# Master/vault account address\nHYPERLIQUID_ACCOUNT_ADDRESS=${cliAccountAddress}\n` : '';
383
430
  const envContent = `# OpenBroker Configuration
384
- # Location: ~/.openbroker/.env
431
+ # Location: ${CONFIG_PATH}
385
432
  # WARNING: Keep this file secret! Never share it!
386
433
 
387
434
  # Your wallet private key
388
435
  HYPERLIQUID_PRIVATE_KEY=${privateKey}
389
-
436
+ ${accountLine}
390
437
  # Network: mainnet or testnet
391
- HYPERLIQUID_NETWORK=mainnet
438
+ HYPERLIQUID_NETWORK=${network}
392
439
  `;
393
440
 
394
441
  fs.writeFileSync(CONFIG_PATH, envContent, { mode: 0o600 });
395
442
  console.log(`✅ Config saved to: ${CONFIG_PATH}\n`);
396
443
 
397
- // Approve builder fee (automatic - no user action needed)
398
- console.log('Step 3/3: Approving builder fee...');
399
- console.log('(This is automatic, and required for trading)\n');
400
-
401
- try {
402
- // Import and run approve-builder inline
403
- const { getClient } = await import('../core/client.js');
404
- const client = getClient();
444
+ // Approve builder fee (automatic - no user action needed; skip on testnet)
445
+ if (useTestnet) {
446
+ console.log('Step 3/3: Skipping builder fee approval (testnet)\n');
447
+ } else {
448
+ console.log('Step 3/3: Approving builder fee...');
449
+ console.log('(This is automatic, and required for trading)\n');
405
450
 
406
- console.log(` Account: ${client.address}`);
407
- console.log(` Builder: ${OPEN_BROKER_BUILDER_ADDRESS}`);
451
+ try {
452
+ // Import and run approve-builder inline
453
+ const { getClient } = await import('../core/client.js');
454
+ const client = getClient();
408
455
 
409
- // Check if already approved
410
- const currentApproval = await client.getMaxBuilderFee(client.address, OPEN_BROKER_BUILDER_ADDRESS);
456
+ console.log(` Account: ${client.address}`);
457
+ console.log(` Builder: ${OPEN_BROKER_BUILDER_ADDRESS}`);
411
458
 
412
- if (currentApproval) {
413
- console.log(`\n✅ Builder fee already approved (${currentApproval})`);
414
- } else {
415
- console.log('\n Sending approval transaction...');
416
- const result = await client.approveBuilderFee('0.1%', OPEN_BROKER_BUILDER_ADDRESS);
459
+ // Check if already approved
460
+ const currentApproval = await client.getMaxBuilderFee(client.address, OPEN_BROKER_BUILDER_ADDRESS);
417
461
 
418
- if (result.status === 'ok') {
419
- console.log('✅ Builder fee approved successfully!');
462
+ if (currentApproval) {
463
+ console.log(`\n✅ Builder fee already approved (${currentApproval})`);
420
464
  } else {
421
- console.log(`⚠️ Approval may have failed: ${result.response}`);
422
- console.log(' You can retry later: openbroker approve-builder');
465
+ console.log('\n Sending approval transaction...');
466
+ const result = await client.approveBuilderFee('0.1%', OPEN_BROKER_BUILDER_ADDRESS);
467
+
468
+ if (result.status === 'ok') {
469
+ console.log('✅ Builder fee approved successfully!');
470
+ } else {
471
+ console.log(`⚠️ Approval may have failed: ${result.response}`);
472
+ console.log(' You can retry later: openbroker approve-builder');
473
+ }
423
474
  }
475
+ } catch (error) {
476
+ console.log(`⚠️ Could not approve builder fee: ${error}`);
477
+ console.log(' You can retry later: openbroker approve-builder');
424
478
  }
425
- } catch (error) {
426
- console.log(`⚠️ Could not approve builder fee: ${error}`);
427
- console.log(' You can retry later: openbroker approve-builder');
428
479
  }
429
480
 
430
481
  // Final summary