openbroker 1.0.33 → 1.0.35
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/CHANGELOG.md +13 -0
- package/README.md +11 -6
- package/SKILL.md +8 -3
- package/bin/openbroker.js +39 -5
- package/package.json +1 -1
- package/scripts/core/client.ts +18 -0
- package/scripts/core/config.ts +92 -30
- package/scripts/core/types.ts +1 -0
- package/scripts/setup/onboard.ts +52 -49
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Open Broker will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.0.34] - 2025-02-05
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
- **Global Config**: Config now stored in `~/.openbroker/.env` for global CLI usage
|
|
9
|
+
- Config loaded from: env vars > local `.env` > `~/.openbroker/.env`
|
|
10
|
+
- `openbroker setup` creates config in home directory
|
|
11
|
+
- Works from any directory without local `.env` file
|
|
12
|
+
- **Read-Only Mode**: Info commands work without configuration
|
|
13
|
+
- Market data, funding rates, search all work immediately
|
|
14
|
+
- Shows warning: "Not configured for trading. Run openbroker setup to enable trades."
|
|
15
|
+
- Trading commands fail with clear error until configured
|
|
16
|
+
- **Better Error Messages**: Clear instructions when config missing
|
|
17
|
+
|
|
5
18
|
## [1.0.3] - 2025-02-05
|
|
6
19
|
|
|
7
20
|
### Added
|
package/README.md
CHANGED
|
@@ -116,12 +116,17 @@ export HYPERLIQUID_NETWORK="testnet"
|
|
|
116
116
|
|
|
117
117
|
## Configuration
|
|
118
118
|
|
|
119
|
-
|
|
119
|
+
Config is loaded from these locations (in order of priority):
|
|
120
|
+
1. Environment variables
|
|
121
|
+
2. `.env` file in current directory
|
|
122
|
+
3. `~/.openbroker/.env` (global config)
|
|
123
|
+
|
|
124
|
+
Run `openbroker setup` to create the global config, or set environment variables:
|
|
120
125
|
|
|
121
126
|
```bash
|
|
122
|
-
HYPERLIQUID_PRIVATE_KEY=0x... # Required: wallet private key
|
|
123
|
-
HYPERLIQUID_NETWORK=mainnet # Optional: mainnet (default) or testnet
|
|
124
|
-
HYPERLIQUID_ACCOUNT_ADDRESS=0x... # Optional: for API wallets
|
|
127
|
+
export HYPERLIQUID_PRIVATE_KEY=0x... # Required: wallet private key
|
|
128
|
+
export HYPERLIQUID_NETWORK=mainnet # Optional: mainnet (default) or testnet
|
|
129
|
+
export HYPERLIQUID_ACCOUNT_ADDRESS=0x... # Optional: for API wallets
|
|
125
130
|
```
|
|
126
131
|
|
|
127
132
|
### API Wallet Setup
|
|
@@ -129,8 +134,8 @@ HYPERLIQUID_ACCOUNT_ADDRESS=0x... # Optional: for API wallets
|
|
|
129
134
|
For automated trading, use an API wallet:
|
|
130
135
|
|
|
131
136
|
```bash
|
|
132
|
-
HYPERLIQUID_PRIVATE_KEY="0x..." # API wallet private key
|
|
133
|
-
HYPERLIQUID_ACCOUNT_ADDRESS="0x..." # Main account address
|
|
137
|
+
export HYPERLIQUID_PRIVATE_KEY="0x..." # API wallet private key
|
|
138
|
+
export HYPERLIQUID_ACCOUNT_ADDRESS="0x..." # Main account address
|
|
134
139
|
```
|
|
135
140
|
|
|
136
141
|
**Note:** Builder fee must be approved with the main wallet first.
|
package/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ license: MIT
|
|
|
5
5
|
compatibility: Requires Node.js 22+, network access to api.hyperliquid.xyz
|
|
6
6
|
metadata:
|
|
7
7
|
author: monemetrics
|
|
8
|
-
version: "1.0.
|
|
8
|
+
version: "1.0.34"
|
|
9
9
|
allowed-tools: Bash(openbroker:*) Bash(npm:*) Read
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -278,9 +278,14 @@ All commands support `--dry` for dry run (preview without executing).
|
|
|
278
278
|
| Percentage down | `--sl -5%` | 5% below entry |
|
|
279
279
|
| Entry price | `--sl entry` | Breakeven stop |
|
|
280
280
|
|
|
281
|
-
##
|
|
281
|
+
## Configuration
|
|
282
282
|
|
|
283
|
-
|
|
283
|
+
Config is loaded from (in priority order):
|
|
284
|
+
1. Environment variables
|
|
285
|
+
2. `.env` in current directory
|
|
286
|
+
3. `~/.openbroker/.env` (global config)
|
|
287
|
+
|
|
288
|
+
Run `openbroker setup` to create the global config interactively.
|
|
284
289
|
|
|
285
290
|
| Variable | Required | Description |
|
|
286
291
|
|----------|----------|-------------|
|
package/bin/openbroker.js
CHANGED
|
@@ -1,24 +1,58 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// This wrapper
|
|
3
|
+
// This wrapper runs the TypeScript CLI using tsx
|
|
4
4
|
import { spawn } from 'child_process';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import path from 'path';
|
|
7
7
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = path.dirname(__filename);
|
|
10
|
+
const packageRoot = path.dirname(__dirname);
|
|
10
11
|
const cliPath = path.join(__dirname, 'cli.ts');
|
|
11
12
|
|
|
12
|
-
// Run the TypeScript CLI with tsx
|
|
13
|
+
// Run the TypeScript CLI with tsx from package's node_modules
|
|
14
|
+
const tsxBin = path.join(packageRoot, 'node_modules', '.bin', 'tsx');
|
|
15
|
+
|
|
13
16
|
const child = spawn(
|
|
14
|
-
|
|
15
|
-
[
|
|
17
|
+
tsxBin,
|
|
18
|
+
[cliPath, ...process.argv.slice(2)],
|
|
16
19
|
{
|
|
17
20
|
stdio: 'inherit',
|
|
18
|
-
cwd:
|
|
21
|
+
cwd: packageRoot,
|
|
22
|
+
env: {
|
|
23
|
+
...process.env,
|
|
24
|
+
// Preserve the original working directory for local .env lookup
|
|
25
|
+
OPENBROKER_CWD: process.cwd(),
|
|
26
|
+
},
|
|
19
27
|
}
|
|
20
28
|
);
|
|
21
29
|
|
|
30
|
+
child.on('error', (err) => {
|
|
31
|
+
// If tsx binary not found, try npx tsx as fallback
|
|
32
|
+
if (err.code === 'ENOENT') {
|
|
33
|
+
const fallback = spawn(
|
|
34
|
+
'npx',
|
|
35
|
+
['tsx', cliPath, ...process.argv.slice(2)],
|
|
36
|
+
{
|
|
37
|
+
stdio: 'inherit',
|
|
38
|
+
cwd: packageRoot,
|
|
39
|
+
env: {
|
|
40
|
+
...process.env,
|
|
41
|
+
OPENBROKER_CWD: process.cwd(),
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
fallback.on('exit', (code) => process.exit(code ?? 0));
|
|
46
|
+
fallback.on('error', () => {
|
|
47
|
+
console.error('Error: tsx not found. Try reinstalling openbroker.');
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
console.error('Error:', err.message);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
22
56
|
child.on('exit', (code) => {
|
|
23
57
|
process.exit(code ?? 0);
|
|
24
58
|
});
|
package/package.json
CHANGED
package/scripts/core/client.ts
CHANGED
|
@@ -78,6 +78,20 @@ export class HyperliquidClient {
|
|
|
78
78
|
return this.config.builderFee / 10; // Convert from tenths of bps to bps
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/** Whether client is in read-only mode (no trading capability) */
|
|
82
|
+
get isReadOnly(): boolean {
|
|
83
|
+
return this.config.isReadOnly;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Throw error if trying to trade in read-only mode */
|
|
87
|
+
private requireTrading(): void {
|
|
88
|
+
if (this.config.isReadOnly) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
'Trading not available. Run "openbroker setup" to configure your wallet.'
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
81
95
|
// ============ Market Data ============
|
|
82
96
|
|
|
83
97
|
async getMetaAndAssetCtxs(): Promise<MetaAndAssetCtxs> {
|
|
@@ -562,6 +576,7 @@ export class HyperliquidClient {
|
|
|
562
576
|
reduceOnly: boolean = false,
|
|
563
577
|
includeBuilder: boolean = true
|
|
564
578
|
): Promise<OrderResponse> {
|
|
579
|
+
this.requireTrading();
|
|
565
580
|
await this.getMetaAndAssetCtxs();
|
|
566
581
|
|
|
567
582
|
const assetIndex = this.getAssetIndex(coin);
|
|
@@ -679,6 +694,7 @@ export class HyperliquidClient {
|
|
|
679
694
|
tpsl: 'tp' | 'sl',
|
|
680
695
|
reduceOnly: boolean = true
|
|
681
696
|
): Promise<OrderResponse> {
|
|
697
|
+
this.requireTrading();
|
|
682
698
|
await this.getMetaAndAssetCtxs();
|
|
683
699
|
|
|
684
700
|
const assetIndex = this.getAssetIndex(coin);
|
|
@@ -766,6 +782,7 @@ export class HyperliquidClient {
|
|
|
766
782
|
}
|
|
767
783
|
|
|
768
784
|
async cancel(coin: string, oid: number): Promise<CancelResponse> {
|
|
785
|
+
this.requireTrading();
|
|
769
786
|
await this.getMetaAndAssetCtxs();
|
|
770
787
|
|
|
771
788
|
const assetIndex = this.getAssetIndex(coin);
|
|
@@ -807,6 +824,7 @@ export class HyperliquidClient {
|
|
|
807
824
|
leverage: number,
|
|
808
825
|
isCross: boolean = true
|
|
809
826
|
): Promise<unknown> {
|
|
827
|
+
this.requireTrading();
|
|
810
828
|
await this.getMetaAndAssetCtxs();
|
|
811
829
|
|
|
812
830
|
const assetIndex = this.getAssetIndex(coin);
|
package/scripts/core/config.ts
CHANGED
|
@@ -1,50 +1,105 @@
|
|
|
1
1
|
// Configuration loader for Open Broker
|
|
2
2
|
|
|
3
3
|
import { config as loadDotenv } from 'dotenv';
|
|
4
|
-
import { resolve } from 'path';
|
|
5
|
-
import { existsSync } from 'fs';
|
|
4
|
+
import { resolve, join } from 'path';
|
|
5
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
6
|
+
import { homedir } from 'os';
|
|
6
7
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
7
8
|
import type { OpenBrokerConfig } from './types.js';
|
|
8
9
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
// Config locations (in order of priority)
|
|
11
|
+
// 1. Environment variables (always checked first by process.env)
|
|
12
|
+
// 2. Local .env in current working directory
|
|
13
|
+
// 3. Global config at ~/.openbroker/.env
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
export const GLOBAL_CONFIG_DIR = join(homedir(), '.openbroker');
|
|
16
|
+
export const GLOBAL_ENV_PATH = join(GLOBAL_CONFIG_DIR, '.env');
|
|
17
|
+
// Use OPENBROKER_CWD if set (from CLI wrapper), otherwise use process.cwd()
|
|
18
|
+
const userCwd = process.env.OPENBROKER_CWD || process.cwd();
|
|
19
|
+
export const LOCAL_ENV_PATH = resolve(userCwd, '.env');
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
console.log(`[DEBUG] .env loaded: ${result.parsed ? 'yes' : 'no'}`);
|
|
22
|
-
}
|
|
23
|
-
} else if (process.env.VERBOSE === '1' || process.env.VERBOSE === 'true') {
|
|
24
|
-
console.log(`[DEBUG] No .env file found at: ${ENV_PATH}`);
|
|
25
|
-
console.log(`[DEBUG] Run 'npx tsx scripts/setup/onboard.ts' to create one`);
|
|
26
|
-
}
|
|
21
|
+
// Package root (for reference, not config loading)
|
|
22
|
+
export const PROJECT_ROOT = resolve(import.meta.dirname, '../..');
|
|
27
23
|
|
|
28
24
|
const MAINNET_URL = 'https://api.hyperliquid.xyz';
|
|
29
25
|
const TESTNET_URL = 'https://api.hyperliquid-testnet.xyz';
|
|
30
26
|
|
|
31
27
|
// Open Broker builder address - receives builder fees on all trades
|
|
32
|
-
// This funds continued development of the open-broker project
|
|
33
28
|
export const OPEN_BROKER_BUILDER_ADDRESS = '0xbb67021fA3e62ab4DA985bb5a55c5c1884381068';
|
|
34
29
|
|
|
30
|
+
// Default read-only key (for info commands that don't need authentication)
|
|
31
|
+
// This is a valid but essentially useless private key
|
|
32
|
+
const DEFAULT_READ_ONLY_KEY = '0x0000000000000000000000000000000000000000000000000000000000000001' as const;
|
|
33
|
+
|
|
34
|
+
// Track if we've shown the read-only warning
|
|
35
|
+
let readOnlyWarningShown = false;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Find and load the config file from multiple locations
|
|
39
|
+
* Priority: env vars > local .env > global ~/.openbroker/.env > project .env
|
|
40
|
+
*/
|
|
41
|
+
function loadEnvFile(): string | null {
|
|
42
|
+
const verbose = process.env.VERBOSE === '1' || process.env.VERBOSE === 'true';
|
|
43
|
+
process.env.DOTENV_CONFIG_QUIET = 'true';
|
|
44
|
+
|
|
45
|
+
// Check locations in order of priority
|
|
46
|
+
const locations = [
|
|
47
|
+
{ path: LOCAL_ENV_PATH, name: 'local (.env)' },
|
|
48
|
+
{ path: GLOBAL_ENV_PATH, name: 'global (~/.openbroker/.env)' },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
for (const { path, name } of locations) {
|
|
52
|
+
if (existsSync(path)) {
|
|
53
|
+
const result = loadDotenv({ path });
|
|
54
|
+
if (verbose) {
|
|
55
|
+
console.log(`[DEBUG] Loaded config from ${name}: ${path}`);
|
|
56
|
+
}
|
|
57
|
+
return path;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (verbose) {
|
|
62
|
+
console.log('[DEBUG] No config file found in any location');
|
|
63
|
+
console.log('[DEBUG] Run "openbroker setup" to configure');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Load config on module import
|
|
70
|
+
const loadedConfigPath = loadEnvFile();
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Ensure the global config directory exists
|
|
74
|
+
*/
|
|
75
|
+
export function ensureConfigDir(): string {
|
|
76
|
+
if (!existsSync(GLOBAL_CONFIG_DIR)) {
|
|
77
|
+
mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
78
|
+
}
|
|
79
|
+
return GLOBAL_CONFIG_DIR;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get the path where config was loaded from, or where it should be saved
|
|
84
|
+
*/
|
|
85
|
+
export function getConfigPath(): string {
|
|
86
|
+
return loadedConfigPath || GLOBAL_ENV_PATH;
|
|
87
|
+
}
|
|
88
|
+
|
|
35
89
|
export function loadConfig(): OpenBrokerConfig {
|
|
36
|
-
|
|
90
|
+
let privateKey = process.env.HYPERLIQUID_PRIVATE_KEY;
|
|
91
|
+
let isReadOnly = false;
|
|
92
|
+
|
|
37
93
|
if (!privateKey) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
94
|
+
// Use default read-only key for info commands
|
|
95
|
+
privateKey = DEFAULT_READ_ONLY_KEY;
|
|
96
|
+
isReadOnly = true;
|
|
97
|
+
|
|
98
|
+
// Show warning once
|
|
99
|
+
if (!readOnlyWarningShown) {
|
|
100
|
+
readOnlyWarningShown = true;
|
|
101
|
+
console.log('\x1b[33m⚠️ Not configured for trading. Run "openbroker setup" to enable trades.\x1b[0m\n');
|
|
43
102
|
}
|
|
44
|
-
throw new Error(
|
|
45
|
-
'HYPERLIQUID_PRIVATE_KEY not found in .env file.\n' +
|
|
46
|
-
'Add it to your .env or run: npx tsx scripts/setup/onboard.ts'
|
|
47
|
-
);
|
|
48
103
|
}
|
|
49
104
|
|
|
50
105
|
if (!privateKey.startsWith('0x') || privateKey.length !== 66) {
|
|
@@ -64,7 +119,6 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
64
119
|
const walletAddress = wallet.address.toLowerCase();
|
|
65
120
|
|
|
66
121
|
// Account address can be different if using an API wallet
|
|
67
|
-
// If not specified, use the wallet address itself
|
|
68
122
|
const accountAddress = process.env.HYPERLIQUID_ACCOUNT_ADDRESS?.toLowerCase();
|
|
69
123
|
|
|
70
124
|
// Determine if this is an API wallet setup
|
|
@@ -76,12 +130,20 @@ export function loadConfig(): OpenBrokerConfig {
|
|
|
76
130
|
walletAddress,
|
|
77
131
|
accountAddress: accountAddress || walletAddress,
|
|
78
132
|
isApiWallet,
|
|
133
|
+
isReadOnly,
|
|
79
134
|
builderAddress,
|
|
80
135
|
builderFee,
|
|
81
136
|
slippageBps,
|
|
82
137
|
};
|
|
83
138
|
}
|
|
84
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Check if the current config is read-only (no trading capability)
|
|
142
|
+
*/
|
|
143
|
+
export function isConfigured(): boolean {
|
|
144
|
+
return !!process.env.HYPERLIQUID_PRIVATE_KEY;
|
|
145
|
+
}
|
|
146
|
+
|
|
85
147
|
export function getNetwork(): 'mainnet' | 'testnet' {
|
|
86
148
|
const network = process.env.HYPERLIQUID_NETWORK || 'mainnet';
|
|
87
149
|
return network === 'testnet' ? 'testnet' : 'mainnet';
|
package/scripts/core/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface OpenBrokerConfig {
|
|
|
8
8
|
walletAddress: string; // Address derived from private key (the signer)
|
|
9
9
|
accountAddress: string; // Address to trade on behalf of (may differ if using API wallet)
|
|
10
10
|
isApiWallet: boolean; // True if walletAddress != accountAddress
|
|
11
|
+
isReadOnly: boolean; // True if using default key (no trading capability)
|
|
11
12
|
builderAddress: string;
|
|
12
13
|
builderFee: number; // tenths of bps (10 = 1 bps)
|
|
13
14
|
slippageBps: number;
|
package/scripts/setup/onboard.ts
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env npx tsx
|
|
2
|
-
// Open Broker - Automated Onboarding
|
|
2
|
+
// Open Broker - Automated Onboarding
|
|
3
3
|
// Creates wallet, configures environment, and approves builder fee
|
|
4
4
|
|
|
5
5
|
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import * as readline from 'readline';
|
|
9
|
-
import {
|
|
9
|
+
import { homedir } from 'os';
|
|
10
10
|
|
|
11
11
|
const OPEN_BROKER_BUILDER_ADDRESS = '0xbb67021fA3e62ab4DA985bb5a55c5c1884381068';
|
|
12
12
|
|
|
13
|
+
// Global config directory: ~/.openbroker/
|
|
14
|
+
const CONFIG_DIR = path.join(homedir(), '.openbroker');
|
|
15
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, '.env');
|
|
16
|
+
|
|
13
17
|
interface OnboardResult {
|
|
14
18
|
success: boolean;
|
|
15
19
|
walletAddress?: string;
|
|
@@ -33,41 +37,39 @@ function prompt(rl: readline.Interface, question: string): Promise<string> {
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
function isValidPrivateKey(key: string): boolean {
|
|
36
|
-
// Check if it's a valid 64-char hex string with 0x prefix
|
|
37
40
|
return /^0x[a-fA-F0-9]{64}$/.test(key);
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return path.resolve(__dirname, '../..');
|
|
43
|
+
function ensureConfigDir(): void {
|
|
44
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
45
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
46
|
+
}
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
async function main(): Promise<OnboardResult> {
|
|
48
|
-
console.log('
|
|
49
|
-
console.log('
|
|
50
|
-
|
|
51
|
-
const projectRoot = getProjectRoot();
|
|
52
|
-
const envPath = path.join(projectRoot, '.env');
|
|
50
|
+
console.log('OpenBroker - Setup');
|
|
51
|
+
console.log('==================\n');
|
|
53
52
|
|
|
54
|
-
// Check if
|
|
55
|
-
if (fs.existsSync(
|
|
56
|
-
console.log('⚠️
|
|
57
|
-
console.log(
|
|
53
|
+
// Check if config already exists
|
|
54
|
+
if (fs.existsSync(CONFIG_PATH)) {
|
|
55
|
+
console.log('⚠️ Config already exists!');
|
|
56
|
+
console.log(` Location: ${CONFIG_PATH}\n`);
|
|
58
57
|
|
|
59
58
|
// Read existing config and show wallet address
|
|
60
|
-
const envContent = fs.readFileSync(
|
|
59
|
+
const envContent = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
|
61
60
|
const keyMatch = envContent.match(/HYPERLIQUID_PRIVATE_KEY=0x([a-fA-F0-9]{64})/);
|
|
62
61
|
|
|
63
62
|
if (keyMatch) {
|
|
64
63
|
const existingKey = `0x${keyMatch[1]}` as `0x${string}`;
|
|
65
64
|
const account = privateKeyToAccount(existingKey);
|
|
66
|
-
console.log('
|
|
67
|
-
console.log('
|
|
65
|
+
console.log('Current Configuration');
|
|
66
|
+
console.log('---------------------');
|
|
68
67
|
console.log(`Wallet Address: ${account.address}`);
|
|
69
|
-
console.log(
|
|
70
|
-
console.log(
|
|
68
|
+
console.log(`Config File: ${CONFIG_PATH}`);
|
|
69
|
+
console.log(`\nTo reconfigure, delete the config file first:`);
|
|
70
|
+
console.log(` rm ${CONFIG_PATH}`);
|
|
71
|
+
console.log(`\nTo fund this wallet, send USDC on Arbitrum, then deposit at:`);
|
|
72
|
+
console.log(` https://app.hyperliquid.xyz/`);
|
|
71
73
|
|
|
72
74
|
return {
|
|
73
75
|
success: true,
|
|
@@ -77,7 +79,7 @@ async function main(): Promise<OnboardResult> {
|
|
|
77
79
|
|
|
78
80
|
return {
|
|
79
81
|
success: false,
|
|
80
|
-
error: 'Invalid
|
|
82
|
+
error: 'Invalid config file - missing or malformed private key',
|
|
81
83
|
};
|
|
82
84
|
}
|
|
83
85
|
|
|
@@ -100,8 +102,7 @@ async function main(): Promise<OnboardResult> {
|
|
|
100
102
|
|
|
101
103
|
if (choice === '1') {
|
|
102
104
|
// User has existing key
|
|
103
|
-
console.log('\nEnter your private key (0x... format)
|
|
104
|
-
console.log('(Input is hidden for security)\n');
|
|
105
|
+
console.log('\nEnter your private key (0x... format):\n');
|
|
105
106
|
|
|
106
107
|
let validKey = false;
|
|
107
108
|
while (!validKey) {
|
|
@@ -130,12 +131,13 @@ async function main(): Promise<OnboardResult> {
|
|
|
130
131
|
const account = privateKeyToAccount(privateKey);
|
|
131
132
|
console.log(`\nWallet Address: ${account.address}\n`);
|
|
132
133
|
|
|
133
|
-
// Create
|
|
134
|
-
console.log('Creating
|
|
134
|
+
// Create config directory and file
|
|
135
|
+
console.log('Creating config...');
|
|
136
|
+
ensureConfigDir();
|
|
135
137
|
|
|
136
|
-
const envContent = `#
|
|
137
|
-
#
|
|
138
|
-
# WARNING: Keep this file secret! Never
|
|
138
|
+
const envContent = `# OpenBroker Configuration
|
|
139
|
+
# Location: ~/.openbroker/.env
|
|
140
|
+
# WARNING: Keep this file secret! Never share it!
|
|
139
141
|
|
|
140
142
|
# Your wallet private key
|
|
141
143
|
HYPERLIQUID_PRIVATE_KEY=${privateKey}
|
|
@@ -143,14 +145,14 @@ HYPERLIQUID_PRIVATE_KEY=${privateKey}
|
|
|
143
145
|
# Network: mainnet or testnet
|
|
144
146
|
HYPERLIQUID_NETWORK=mainnet
|
|
145
147
|
|
|
146
|
-
# Builder fee
|
|
148
|
+
# Builder fee (supports openbroker development)
|
|
147
149
|
# Default: 1 bps (0.01%) on trades
|
|
148
150
|
BUILDER_ADDRESS=${OPEN_BROKER_BUILDER_ADDRESS}
|
|
149
151
|
BUILDER_FEE=10
|
|
150
152
|
`;
|
|
151
153
|
|
|
152
|
-
fs.writeFileSync(
|
|
153
|
-
console.log(`✅
|
|
154
|
+
fs.writeFileSync(CONFIG_PATH, envContent, { mode: 0o600 });
|
|
155
|
+
console.log(`✅ Config saved to: ${CONFIG_PATH}\n`);
|
|
154
156
|
|
|
155
157
|
// Approve builder fee
|
|
156
158
|
console.log('Approving builder fee...');
|
|
@@ -177,48 +179,49 @@ BUILDER_FEE=10
|
|
|
177
179
|
console.log('✅ Builder fee approved successfully!');
|
|
178
180
|
} else {
|
|
179
181
|
console.log(`⚠️ Approval may have failed: ${result.response}`);
|
|
180
|
-
console.log(' You can retry later:
|
|
182
|
+
console.log(' You can retry later: openbroker approve-builder');
|
|
181
183
|
}
|
|
182
184
|
}
|
|
183
185
|
} catch (error) {
|
|
184
186
|
console.log(`⚠️ Could not approve builder fee: ${error}`);
|
|
185
|
-
console.log(' You can retry later:
|
|
187
|
+
console.log(' You can retry later: openbroker approve-builder');
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
// Final summary
|
|
189
191
|
console.log('\n========================================');
|
|
190
|
-
console.log('
|
|
192
|
+
console.log(' SETUP COMPLETE! ');
|
|
191
193
|
console.log('========================================\n');
|
|
192
194
|
|
|
193
195
|
console.log('Your Trading Wallet');
|
|
194
196
|
console.log('-------------------');
|
|
195
197
|
console.log(`Address: ${account.address}`);
|
|
196
198
|
console.log(`Network: Hyperliquid (Mainnet)`);
|
|
199
|
+
console.log(`Config: ${CONFIG_PATH}`);
|
|
197
200
|
|
|
198
201
|
if (choice === '2') {
|
|
199
202
|
console.log('\n⚠️ IMPORTANT: Save your private key!');
|
|
200
203
|
console.log('-----------------------------------');
|
|
201
204
|
console.log(`Private Key: ${privateKey}`);
|
|
202
|
-
console.log('\nThis key is stored in
|
|
205
|
+
console.log('\nThis key is stored in ~/.openbroker/.env');
|
|
206
|
+
console.log('Back it up securely - if lost, funds cannot be recovered!');
|
|
203
207
|
}
|
|
204
208
|
|
|
205
|
-
console.log('\n📋 Next
|
|
206
|
-
console.log('
|
|
207
|
-
console.log('1.
|
|
209
|
+
console.log('\n📋 Next Steps');
|
|
210
|
+
console.log('--------------');
|
|
211
|
+
console.log('1. Fund your wallet with USDC on Arbitrum:');
|
|
208
212
|
console.log(` ${account.address}`);
|
|
209
213
|
console.log('');
|
|
210
214
|
console.log('2. Deposit USDC to Hyperliquid:');
|
|
211
215
|
console.log(' https://app.hyperliquid.xyz/');
|
|
212
|
-
console.log(' (Connect wallet → Deposit → Select amount)');
|
|
213
216
|
console.log('');
|
|
214
217
|
console.log('3. Start trading!');
|
|
215
|
-
console.log('
|
|
216
|
-
console.log('
|
|
218
|
+
console.log(' openbroker account');
|
|
219
|
+
console.log(' openbroker buy --coin ETH --size 0.01 --dry');
|
|
217
220
|
|
|
218
|
-
console.log('\n⚠️
|
|
219
|
-
console.log('
|
|
220
|
-
console.log(
|
|
221
|
-
console.log('
|
|
221
|
+
console.log('\n⚠️ Security');
|
|
222
|
+
console.log('------------');
|
|
223
|
+
console.log(`Config stored at: ${CONFIG_PATH}`);
|
|
224
|
+
console.log('Never share this file or your private key!');
|
|
222
225
|
|
|
223
226
|
return {
|
|
224
227
|
success: true,
|
|
@@ -233,10 +236,10 @@ export { main as onboard };
|
|
|
233
236
|
// Run if executed directly
|
|
234
237
|
main().then(result => {
|
|
235
238
|
if (!result.success) {
|
|
236
|
-
console.error(`\
|
|
239
|
+
console.error(`\nSetup failed: ${result.error}`);
|
|
237
240
|
process.exit(1);
|
|
238
241
|
}
|
|
239
242
|
}).catch(error => {
|
|
240
|
-
console.error('
|
|
243
|
+
console.error('Setup error:', error);
|
|
241
244
|
process.exit(1);
|
|
242
245
|
});
|