openbroker 1.0.80 → 1.0.82
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/bin/cli.ts +22 -1
- package/bin/openbroker.js +4 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/auto/cli.ts +9 -1
- package/scripts/auto/loader.ts +37 -3
- package/scripts/auto/runtime.ts +5 -4
- package/scripts/core/client.ts +603 -214
- package/scripts/core/config.ts +19 -0
- package/scripts/core/types.ts +1 -0
- package/scripts/core/utils.ts +4 -1
- package/scripts/info/account.ts +12 -8
- package/scripts/info/all-markets.ts +3 -0
- package/scripts/info/candles.ts +3 -0
- package/scripts/info/funding-history.ts +3 -0
- package/scripts/info/funding-scan.ts +4 -0
- package/scripts/info/funding.ts +7 -0
- package/scripts/info/markets.ts +7 -0
- package/scripts/info/orders.ts +5 -0
- package/scripts/info/search-markets.ts +8 -0
- package/scripts/info/trades.ts +3 -0
- package/scripts/setup/onboard.ts +95 -44
package/scripts/core/config.ts
CHANGED
|
@@ -42,6 +42,20 @@ function loadEnvFile(): string | null {
|
|
|
42
42
|
const verbose = process.env.VERBOSE === '1' || process.env.VERBOSE === 'true';
|
|
43
43
|
process.env.DOTENV_CONFIG_QUIET = 'true';
|
|
44
44
|
|
|
45
|
+
// Explicit config path via -c / --config (highest priority — overrides shell env vars)
|
|
46
|
+
const explicitPath = process.env.OPENBROKER_CONFIG;
|
|
47
|
+
if (explicitPath) {
|
|
48
|
+
if (existsSync(explicitPath)) {
|
|
49
|
+
loadDotenv({ path: explicitPath, override: true });
|
|
50
|
+
if (verbose) {
|
|
51
|
+
console.log(`[DEBUG] Loaded config from -c flag: ${explicitPath}`);
|
|
52
|
+
}
|
|
53
|
+
return explicitPath;
|
|
54
|
+
}
|
|
55
|
+
console.error(`Config file not found: ${explicitPath}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
// Check locations in order of priority
|
|
46
60
|
const locations = [
|
|
47
61
|
{ path: LOCAL_ENV_PATH, name: 'local (.env)' },
|
|
@@ -124,6 +138,10 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
124
138
|
// Determine if this is an API wallet setup
|
|
125
139
|
const isApiWallet = accountAddress !== undefined && accountAddress !== walletAddress;
|
|
126
140
|
|
|
141
|
+
// Vault address — only for vault trading (ERC4626 contracts / native vaults via CoreWriter).
|
|
142
|
+
// Standard API wallets (approved via approveAgent) do NOT need this.
|
|
143
|
+
const vaultAddress = process.env.HYPERLIQUID_VAULT_ADDRESS?.toLowerCase();
|
|
144
|
+
|
|
127
145
|
return {
|
|
128
146
|
baseUrl,
|
|
129
147
|
privateKey: privateKey as `0x${string}`,
|
|
@@ -134,6 +152,7 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
134
152
|
builderAddress,
|
|
135
153
|
builderFee,
|
|
136
154
|
slippageBps,
|
|
155
|
+
vaultAddress,
|
|
137
156
|
};
|
|
138
157
|
}
|
|
139
158
|
|
package/scripts/core/types.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface OpenBrokerConfig {
|
|
|
12
12
|
builderAddress: string;
|
|
13
13
|
builderFee: number; // tenths of bps (10 = 1 bps)
|
|
14
14
|
slippageBps: number;
|
|
15
|
+
vaultAddress?: string; // Explicit vault address for vault trading (ERC4626 / native vaults via CoreWriter)
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
// ============ Builder ============
|
package/scripts/core/utils.ts
CHANGED
|
@@ -155,8 +155,11 @@ export function generateCloid(): string {
|
|
|
155
155
|
* Returns true if approved, false if not
|
|
156
156
|
*/
|
|
157
157
|
export async function checkBuilderFeeApproval(
|
|
158
|
-
client: { getMaxBuilderFee: () => Promise<string | null>; builderAddress: string }
|
|
158
|
+
client: { getMaxBuilderFee: () => Promise<string | null>; builderAddress: string; isTestnet: boolean }
|
|
159
159
|
): Promise<boolean> {
|
|
160
|
+
// Skip builder fee check on testnet — builder may not have balance
|
|
161
|
+
if (client.isTestnet) return true;
|
|
162
|
+
|
|
160
163
|
const approval = await client.getMaxBuilderFee();
|
|
161
164
|
if (!approval) {
|
|
162
165
|
console.log('⚠️ Builder fee not approved!');
|
package/scripts/info/account.ts
CHANGED
|
@@ -123,15 +123,19 @@ async function main() {
|
|
|
123
123
|
console.log(`Account Mode: ${modeLabel[accountMode] ?? accountMode}`);
|
|
124
124
|
|
|
125
125
|
if (!isOtherAccount) {
|
|
126
|
-
// Check builder fee approval
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
console.log(`Builder Fee: ${client.builderFeeBps} bps`);
|
|
130
|
-
if (builderApproval) {
|
|
131
|
-
console.log(`Builder Approved: ✅ Yes (max: ${builderApproval})`);
|
|
126
|
+
// Check builder fee approval (skip on testnet)
|
|
127
|
+
if (client.isTestnet) {
|
|
128
|
+
console.log(`Builder Fee: skipped (testnet)`);
|
|
132
129
|
} else {
|
|
133
|
-
|
|
134
|
-
console.log(
|
|
130
|
+
const builderApproval = await client.getMaxBuilderFee();
|
|
131
|
+
console.log(`Builder Address: ${client.builderAddress}`);
|
|
132
|
+
console.log(`Builder Fee: ${client.builderFeeBps} bps`);
|
|
133
|
+
if (builderApproval) {
|
|
134
|
+
console.log(`Builder Approved: ✅ Yes (max: ${builderApproval})`);
|
|
135
|
+
} else {
|
|
136
|
+
console.log(`Builder Approved: ❌ No`);
|
|
137
|
+
console.log(`\n⚠️ Run: npx tsx scripts/setup/approve-builder.ts`);
|
|
138
|
+
}
|
|
135
139
|
}
|
|
136
140
|
|
|
137
141
|
// Warn if API wallet setup looks misconfigured
|
|
@@ -108,6 +108,9 @@ async function main() {
|
|
|
108
108
|
|
|
109
109
|
// Fetch HIP-3 perps
|
|
110
110
|
if (args.type === 'all' || args.type === 'hip3') {
|
|
111
|
+
if (client.isTestnet) {
|
|
112
|
+
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Use "dexName:COIN" syntax to load a specific dex.\n');
|
|
113
|
+
}
|
|
111
114
|
try {
|
|
112
115
|
const allPerpMetas = await client.getAllPerpMetas();
|
|
113
116
|
// Skip index 0 (main dex), process HIP-3 dexs
|
package/scripts/info/candles.ts
CHANGED
|
@@ -67,6 +67,9 @@ async function main() {
|
|
|
67
67
|
try {
|
|
68
68
|
// Load metadata (needed for HIP-3 coin resolution)
|
|
69
69
|
await client.getMetaAndAssetCtxs();
|
|
70
|
+
if (client.isTestnet && coin.includes(':')) {
|
|
71
|
+
await client.loadSingleHip3Dex(coin.split(':')[0]);
|
|
72
|
+
}
|
|
70
73
|
|
|
71
74
|
const now = Date.now();
|
|
72
75
|
const startTime = now - (bars * (INTERVAL_MS[interval] || 3_600_000));
|
|
@@ -43,6 +43,9 @@ async function main() {
|
|
|
43
43
|
try {
|
|
44
44
|
// Load metadata (needed for HIP-3 coin resolution)
|
|
45
45
|
await client.getMetaAndAssetCtxs();
|
|
46
|
+
if (client.isTestnet && coin.includes(':')) {
|
|
47
|
+
await client.loadSingleHip3Dex(coin.split(':')[0]);
|
|
48
|
+
}
|
|
46
49
|
|
|
47
50
|
const now = Date.now();
|
|
48
51
|
const startTime = now - (hours * 3_600_000);
|
|
@@ -184,6 +184,10 @@ async function main() {
|
|
|
184
184
|
console.log('==================================\n');
|
|
185
185
|
console.log(`Threshold: ${threshold}% annualized | Scope: ${mainOnly ? 'main only' : hip3Only ? 'HIP-3 only' : 'all dexes'}\n`);
|
|
186
186
|
|
|
187
|
+
if (client.isTestnet && !mainOnly) {
|
|
188
|
+
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Only main perps will be scanned.\n');
|
|
189
|
+
}
|
|
190
|
+
|
|
187
191
|
const options = { threshold, mainOnly, hip3Only, topN };
|
|
188
192
|
|
|
189
193
|
const runScan = async () => {
|
package/scripts/info/funding.ts
CHANGED
|
@@ -56,7 +56,14 @@ async function main() {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
// Include HIP-3 dex assets
|
|
59
|
+
if (client.isTestnet && (includeHip3 || showAll) && !(filterCoin?.includes(':'))) {
|
|
60
|
+
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Use --coin dexName:COIN to view a specific HIP-3 asset.\n');
|
|
61
|
+
}
|
|
59
62
|
if (includeHip3 || showAll || (filterCoin && filterCoin.includes(':'))) {
|
|
63
|
+
// On testnet, load the specific dex on demand if user specified dex:COIN
|
|
64
|
+
if (client.isTestnet && filterCoin?.includes(':')) {
|
|
65
|
+
await client.loadSingleHip3Dex(filterCoin.split(':')[0]);
|
|
66
|
+
}
|
|
60
67
|
const allPerps = await client.getAllPerpMetas();
|
|
61
68
|
for (const dexData of allPerps) {
|
|
62
69
|
if (!dexData.dexName) continue; // Skip main dex (already loaded)
|
package/scripts/info/markets.ts
CHANGED
|
@@ -59,7 +59,14 @@ async function main() {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// Include HIP-3 markets
|
|
62
|
+
if (client.isTestnet && includeHip3 && !(filterCoin?.includes(':'))) {
|
|
63
|
+
console.log('Note: Testnet — HIP-3 dexes not auto-loaded. Use --coin dexName:COIN to view a specific HIP-3 asset.\n');
|
|
64
|
+
}
|
|
62
65
|
if (includeHip3 || (filterCoin && filterCoin.includes(':'))) {
|
|
66
|
+
// On testnet, load the specific dex on demand if user specified dex:COIN
|
|
67
|
+
if (client.isTestnet && filterCoin?.includes(':')) {
|
|
68
|
+
await client.loadSingleHip3Dex(filterCoin.split(':')[0]);
|
|
69
|
+
}
|
|
63
70
|
const allPerps = await client.getAllPerpMetas();
|
|
64
71
|
for (const dexData of allPerps) {
|
|
65
72
|
if (!dexData.dexName) continue;
|
package/scripts/info/orders.ts
CHANGED
|
@@ -45,6 +45,11 @@ async function main() {
|
|
|
45
45
|
const lookupAddress = targetAddress?.toLowerCase();
|
|
46
46
|
|
|
47
47
|
try {
|
|
48
|
+
// On testnet, load specific HIP-3 dex on demand if filtering by dex:COIN
|
|
49
|
+
if (client.isTestnet && filterCoin?.includes(':')) {
|
|
50
|
+
await client.loadSingleHip3Dex(filterCoin.split(':')[0]);
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
if (openOnly) {
|
|
49
54
|
// Use the dedicated open orders endpoint
|
|
50
55
|
let openOrders = await client.getOpenOrders(lookupAddress);
|
|
@@ -122,6 +122,14 @@ async function main() {
|
|
|
122
122
|
|
|
123
123
|
// Search HIP-3 perps
|
|
124
124
|
if (args.type === 'all' || args.type === 'hip3') {
|
|
125
|
+
if (client.isTestnet) {
|
|
126
|
+
// On testnet, load specific dex if query is "dex:COIN" format
|
|
127
|
+
if (args.query.includes(':')) {
|
|
128
|
+
await client.loadSingleHip3Dex(args.query.split(':')[0]);
|
|
129
|
+
} else {
|
|
130
|
+
console.log(' (Testnet: HIP-3 dexes not auto-loaded. Use "dexName:COIN" to search a specific dex.)\n');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
125
133
|
try {
|
|
126
134
|
const allPerpMetas = await client.getAllPerpMetas();
|
|
127
135
|
// Skip index 0 (main dex), process HIP-3 dexs
|
package/scripts/info/trades.ts
CHANGED
|
@@ -43,6 +43,9 @@ async function main() {
|
|
|
43
43
|
try {
|
|
44
44
|
// Load metadata (needed for HIP-3 coin resolution)
|
|
45
45
|
await client.getMetaAndAssetCtxs();
|
|
46
|
+
if (client.isTestnet && coin.includes(':')) {
|
|
47
|
+
await client.loadSingleHip3Dex(coin.split(':')[0]);
|
|
48
|
+
}
|
|
46
49
|
|
|
47
50
|
let trades = await client.getRecentTrades(normalizeCoin(coin));
|
|
48
51
|
|
package/scripts/setup/onboard.ts
CHANGED
|
@@ -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
|
|
16
|
-
const
|
|
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(
|
|
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:
|
|
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
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
180
|
-
|
|
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
|
-
|
|
183
|
-
|
|
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('
|
|
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:
|
|
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
|
|
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
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
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
|
-
|
|
407
|
-
|
|
451
|
+
try {
|
|
452
|
+
// Import and run approve-builder inline
|
|
453
|
+
const { getClient } = await import('../core/client.js');
|
|
454
|
+
const client = getClient();
|
|
408
455
|
|
|
409
|
-
|
|
410
|
-
|
|
456
|
+
console.log(` Account: ${client.address}`);
|
|
457
|
+
console.log(` Builder: ${OPEN_BROKER_BUILDER_ADDRESS}`);
|
|
411
458
|
|
|
412
|
-
|
|
413
|
-
|
|
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 (
|
|
419
|
-
console.log(
|
|
462
|
+
if (currentApproval) {
|
|
463
|
+
console.log(`\n✅ Builder fee already approved (${currentApproval})`);
|
|
420
464
|
} else {
|
|
421
|
-
console.log(
|
|
422
|
-
|
|
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
|