@rabby-wallet/hyperliquid-sdk 1.0.0-beta.9 → 1.0.1
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/dist/client/exchange-client.d.ts +2 -1
- package/dist/client/exchange-client.js +31 -28
- package/dist/client/http-client.js +1 -1
- package/dist/client/info-client.d.ts +12 -7
- package/dist/client/info-client.js +67 -15
- package/dist/client/websocket-client.d.ts +14 -3
- package/dist/client/websocket-client.js +57 -2
- package/dist/hyperliquid-sdk.d.ts +3 -3
- package/dist/hyperliquid-sdk.js +19 -6
- package/dist/types/constants.d.ts +2 -1
- package/dist/types/constants.js +2 -1
- package/dist/types/index.d.ts +32 -0
- package/dist/utils/number.d.ts +12 -0
- package/dist/utils/number.js +22 -0
- package/package.json +1 -1
|
@@ -9,10 +9,11 @@ export declare class ExchangeClient {
|
|
|
9
9
|
private agentPublicKey?;
|
|
10
10
|
private agentName?;
|
|
11
11
|
private readonly isTestnet;
|
|
12
|
-
private
|
|
12
|
+
private masterAddress;
|
|
13
13
|
private builder?;
|
|
14
14
|
private readonly symbolConversion;
|
|
15
15
|
constructor(config: ExchangeClientConfig);
|
|
16
|
+
initAccount(masterAddress: string, agentPrivateKey: string, agentPublicKey: string, agentName?: string): void;
|
|
16
17
|
/**
|
|
17
18
|
* Get the wallet address
|
|
18
19
|
*/
|
|
@@ -48,6 +48,7 @@ const http_client_1 = require("./http-client");
|
|
|
48
48
|
const constants_1 = require("../types/constants");
|
|
49
49
|
const signer_1 = require("../utils/signer");
|
|
50
50
|
const symbolConversion_1 = require("./symbolConversion");
|
|
51
|
+
const number_1 = require("../utils/number");
|
|
51
52
|
/**
|
|
52
53
|
* Client for executing trades on Hyperliquid (perpetuals only)
|
|
53
54
|
* Only includes essential trading APIs as specified
|
|
@@ -79,6 +80,12 @@ class ExchangeClient {
|
|
|
79
80
|
}
|
|
80
81
|
this.isTestnet = config.isTestnet || false;
|
|
81
82
|
}
|
|
83
|
+
initAccount(masterAddress, agentPrivateKey, agentPublicKey, agentName) {
|
|
84
|
+
this.masterAddress = masterAddress;
|
|
85
|
+
this.agentPrivateKey = agentPrivateKey.startsWith('0x') ? agentPrivateKey : ethUtil.addHexPrefix(agentPrivateKey);
|
|
86
|
+
this.agentPublicKey = agentPublicKey.startsWith('0x') ? agentPublicKey : ethUtil.addHexPrefix(agentPublicKey);
|
|
87
|
+
this.agentName = agentName || '';
|
|
88
|
+
}
|
|
82
89
|
/**
|
|
83
90
|
* Get the wallet address
|
|
84
91
|
*/
|
|
@@ -163,7 +170,7 @@ class ExchangeClient {
|
|
|
163
170
|
const decimals = ((_a = midPx.toString().split('.')[1]) === null || _a === void 0 ? void 0 : _a.length) || 0;
|
|
164
171
|
const prepDecimals = Math.max(0, decimals - 1);
|
|
165
172
|
const px = isBuy ? (Number(midPx) * (1 + slippage)).toFixed(prepDecimals) : (Number(midPx) * (1 - slippage)).toFixed(prepDecimals);
|
|
166
|
-
return px;
|
|
173
|
+
return (0, number_1.removeTrailingZeros)(px);
|
|
167
174
|
}
|
|
168
175
|
/**
|
|
169
176
|
* Place a market order
|
|
@@ -174,11 +181,11 @@ class ExchangeClient {
|
|
|
174
181
|
return __awaiter(this, void 0, void 0, function* () {
|
|
175
182
|
try {
|
|
176
183
|
const slippage = params.slippage || constants_1.SLIPPAGE;
|
|
177
|
-
const px = this.getSlippagePx(params.midPx, slippage, params.isBuy);
|
|
184
|
+
const px = this.getSlippagePx((0, number_1.removeTrailingZeros)(params.midPx), slippage, params.isBuy);
|
|
178
185
|
const orders = [{
|
|
179
186
|
coin: params.coin,
|
|
180
187
|
isBuy: params.isBuy,
|
|
181
|
-
sz: params.size,
|
|
188
|
+
sz: (0, number_1.removeTrailingZeros)(params.size),
|
|
182
189
|
limitPx: px,
|
|
183
190
|
reduceOnly: false,
|
|
184
191
|
orderType: { limit: { tif: 'Ioc' } },
|
|
@@ -187,13 +194,13 @@ class ExchangeClient {
|
|
|
187
194
|
const tpOrder = {
|
|
188
195
|
coin: params.coin,
|
|
189
196
|
isBuy: !params.isBuy,
|
|
190
|
-
sz: params.size,
|
|
191
|
-
limitPx: params.tpTriggerPx,
|
|
197
|
+
sz: (0, number_1.removeTrailingZeros)(params.size),
|
|
198
|
+
limitPx: (0, number_1.removeTrailingZeros)(params.tpTriggerPx),
|
|
192
199
|
reduceOnly: true,
|
|
193
200
|
orderType: {
|
|
194
201
|
trigger: {
|
|
195
202
|
isMarket: true,
|
|
196
|
-
triggerPx: params.tpTriggerPx,
|
|
203
|
+
triggerPx: (0, number_1.removeTrailingZeros)(params.tpTriggerPx),
|
|
197
204
|
tpsl: 'tp',
|
|
198
205
|
}
|
|
199
206
|
}
|
|
@@ -204,13 +211,13 @@ class ExchangeClient {
|
|
|
204
211
|
const slOrder = {
|
|
205
212
|
coin: params.coin,
|
|
206
213
|
isBuy: !params.isBuy,
|
|
207
|
-
sz: params.size,
|
|
208
|
-
limitPx: params.slTriggerPx,
|
|
214
|
+
sz: (0, number_1.removeTrailingZeros)(params.size),
|
|
215
|
+
limitPx: (0, number_1.removeTrailingZeros)(params.slTriggerPx),
|
|
209
216
|
reduceOnly: true,
|
|
210
217
|
orderType: {
|
|
211
218
|
trigger: {
|
|
212
219
|
isMarket: true,
|
|
213
|
-
triggerPx: params.slTriggerPx,
|
|
220
|
+
triggerPx: (0, number_1.removeTrailingZeros)(params.slTriggerPx),
|
|
214
221
|
tpsl: 'sl',
|
|
215
222
|
}
|
|
216
223
|
}
|
|
@@ -235,7 +242,7 @@ class ExchangeClient {
|
|
|
235
242
|
const orders = [{
|
|
236
243
|
coin: params.coin,
|
|
237
244
|
isBuy: params.isBuy,
|
|
238
|
-
sz: params.size,
|
|
245
|
+
sz: (0, number_1.removeTrailingZeros)(params.size),
|
|
239
246
|
limitPx: px,
|
|
240
247
|
reduceOnly: true,
|
|
241
248
|
orderType: { limit: { tif: 'Ioc' } },
|
|
@@ -266,8 +273,8 @@ class ExchangeClient {
|
|
|
266
273
|
return {
|
|
267
274
|
a: assetIndex,
|
|
268
275
|
b: order.isBuy,
|
|
269
|
-
p: order.limitPx,
|
|
270
|
-
s: order.sz,
|
|
276
|
+
p: (0, number_1.removeTrailingZeros)(order.limitPx),
|
|
277
|
+
s: (0, number_1.removeTrailingZeros)(order.sz),
|
|
271
278
|
r: order.reduceOnly || false,
|
|
272
279
|
t: order.orderType || { limit: { tif: 'Gtc' } },
|
|
273
280
|
};
|
|
@@ -299,12 +306,12 @@ class ExchangeClient {
|
|
|
299
306
|
coin: params.coin,
|
|
300
307
|
isBuy: !params.isBuy,
|
|
301
308
|
sz: '0',
|
|
302
|
-
limitPx: this.getSlippagePx(params.tpTriggerPx, constants_1.SLIPPAGE, !params.isBuy),
|
|
309
|
+
limitPx: this.getSlippagePx((0, number_1.removeTrailingZeros)(params.tpTriggerPx), constants_1.SLIPPAGE, !params.isBuy),
|
|
303
310
|
reduceOnly: true,
|
|
304
311
|
orderType: {
|
|
305
312
|
trigger: {
|
|
306
313
|
isMarket: true,
|
|
307
|
-
triggerPx: params.tpTriggerPx,
|
|
314
|
+
triggerPx: (0, number_1.removeTrailingZeros)(params.tpTriggerPx),
|
|
308
315
|
tpsl: 'tp',
|
|
309
316
|
}
|
|
310
317
|
}
|
|
@@ -315,12 +322,12 @@ class ExchangeClient {
|
|
|
315
322
|
coin: params.coin,
|
|
316
323
|
isBuy: !params.isBuy,
|
|
317
324
|
sz: '0',
|
|
318
|
-
limitPx: this.getSlippagePx(params.slTriggerPx, constants_1.SLIPPAGE, !params.isBuy),
|
|
325
|
+
limitPx: this.getSlippagePx((0, number_1.removeTrailingZeros)(params.slTriggerPx), constants_1.SLIPPAGE, !params.isBuy),
|
|
319
326
|
reduceOnly: true,
|
|
320
327
|
orderType: {
|
|
321
328
|
trigger: {
|
|
322
329
|
isMarket: true,
|
|
323
|
-
triggerPx: params.slTriggerPx,
|
|
330
|
+
triggerPx: (0, number_1.removeTrailingZeros)(params.slTriggerPx),
|
|
324
331
|
tpsl: 'sl',
|
|
325
332
|
}
|
|
326
333
|
}
|
|
@@ -371,8 +378,8 @@ class ExchangeClient {
|
|
|
371
378
|
order: {
|
|
372
379
|
a: yield this.symbolConversion.getAssetIndex(params.coin),
|
|
373
380
|
b: params.isBuy,
|
|
374
|
-
p: params.limitPx,
|
|
375
|
-
s: params.sz,
|
|
381
|
+
p: (0, number_1.removeTrailingZeros)(params.limitPx),
|
|
382
|
+
s: (0, number_1.removeTrailingZeros)(params.sz),
|
|
376
383
|
r: params.reduceOnly || false,
|
|
377
384
|
t: params.orderType || { limit: { tif: 'Gtc' } },
|
|
378
385
|
},
|
|
@@ -469,19 +476,17 @@ class ExchangeClient {
|
|
|
469
476
|
const action = {
|
|
470
477
|
type: constants_1.ExchangeType.APPROVE_BUILDER_FEE,
|
|
471
478
|
hyperliquidChain: this.isTestnet ? 'Testnet' : 'Mainnet',
|
|
472
|
-
signatureChainId: this.isTestnet ? '0x66eee' : '
|
|
479
|
+
signatureChainId: this.isTestnet ? '0x66eee' : '0xa4b1',
|
|
480
|
+
maxFeeRate: params.maxFee || '0.1%',
|
|
473
481
|
builder: params.builder.toLowerCase(),
|
|
474
|
-
maxFee: params.maxFee || '0.1%',
|
|
475
482
|
nonce,
|
|
476
483
|
};
|
|
477
484
|
const { domain, types, primaryType, message } = (0, signer_1.prepareMasterSignData)({
|
|
478
485
|
action,
|
|
479
486
|
payloadTypes: [
|
|
480
|
-
{ name: 'type', type: 'string' },
|
|
481
487
|
{ name: 'hyperliquidChain', type: 'string' },
|
|
482
|
-
{ name: '
|
|
483
|
-
{ name: 'builder', type: '
|
|
484
|
-
{ name: 'maxFee', type: 'string' },
|
|
488
|
+
{ name: 'maxFeeRate', type: 'string' },
|
|
489
|
+
{ name: 'builder', type: 'address' },
|
|
485
490
|
{ name: 'nonce', type: 'uint64' },
|
|
486
491
|
],
|
|
487
492
|
primaryType: 'HyperliquidTransaction:ApproveBuilderFee',
|
|
@@ -518,17 +523,15 @@ class ExchangeClient {
|
|
|
518
523
|
const action = {
|
|
519
524
|
type: constants_1.ExchangeType.WITHDRAW,
|
|
520
525
|
hyperliquidChain: this.isTestnet ? 'Testnet' : 'Mainnet',
|
|
521
|
-
signatureChainId: this.isTestnet ? '0x66eee' : '
|
|
526
|
+
signatureChainId: this.isTestnet ? '0x66eee' : '0xa4b1',
|
|
522
527
|
destination: params.destination,
|
|
523
528
|
amount: params.amount,
|
|
524
|
-
time:
|
|
529
|
+
time: nonce,
|
|
525
530
|
};
|
|
526
531
|
const { domain, types, primaryType, message } = (0, signer_1.prepareMasterSignData)({
|
|
527
532
|
action,
|
|
528
533
|
payloadTypes: [
|
|
529
|
-
{ name: 'type', type: 'string' },
|
|
530
534
|
{ name: 'hyperliquidChain', type: 'string' },
|
|
531
|
-
{ name: 'signatureChainId', type: 'string' },
|
|
532
535
|
{ name: 'destination', type: 'string' },
|
|
533
536
|
{ name: 'amount', type: 'string' },
|
|
534
537
|
{ name: 'time', type: 'uint64' },
|
|
@@ -41,7 +41,7 @@ class HttpClient {
|
|
|
41
41
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
42
42
|
}
|
|
43
43
|
const result = yield response.json();
|
|
44
|
-
if (result.status === 'error') {
|
|
44
|
+
if (result.status === 'error' || result.status === 'fail' || result.status === 'err') {
|
|
45
45
|
throw new Error(JSON.stringify(result.response));
|
|
46
46
|
}
|
|
47
47
|
return result;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Meta, AssetCtx, ClearinghouseState, UserFills, CandleSnapshot, AllMids, ExtraAgent, OpenOrder, FeeResponse, UserNonFundingLedgerUpdates } from '../types';
|
|
1
|
+
import type { Meta, AssetCtx, ClearinghouseState, UserFills, CandleSnapshot, AllMids, ExtraAgent, OpenOrder, FeeResponse, UserNonFundingLedgerUpdates, UserHistoricalOrders } from '../types';
|
|
2
2
|
export interface InfoClientConfig {
|
|
3
3
|
isTestnet?: boolean;
|
|
4
4
|
timeout?: number;
|
|
5
|
-
masterAddress
|
|
5
|
+
masterAddress?: string;
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
8
|
* Client for querying Hyperliquid info endpoints (perpetuals only)
|
|
@@ -10,7 +10,7 @@ export interface InfoClientConfig {
|
|
|
10
10
|
*/
|
|
11
11
|
export declare class InfoClient {
|
|
12
12
|
private readonly httpClient;
|
|
13
|
-
private
|
|
13
|
+
private masterAddress?;
|
|
14
14
|
private metaAndAssetCtxsCache;
|
|
15
15
|
private metaAndAssetCtxsInFlight;
|
|
16
16
|
constructor(config: InfoClientConfig);
|
|
@@ -18,6 +18,7 @@ export declare class InfoClient {
|
|
|
18
18
|
* Get all mid prices for all assets
|
|
19
19
|
*/
|
|
20
20
|
getAllMids(): Promise<AllMids>;
|
|
21
|
+
initMasterAddress(masterAddress: string): void;
|
|
21
22
|
/**
|
|
22
23
|
* Get metadata and asset contexts
|
|
23
24
|
*/
|
|
@@ -28,16 +29,20 @@ export declare class InfoClient {
|
|
|
28
29
|
/**
|
|
29
30
|
* Get user's fill history
|
|
30
31
|
*/
|
|
31
|
-
getUserFills(address?: string): Promise<UserFills>;
|
|
32
|
+
getUserFills(address?: string, aggregateByTime?: boolean): Promise<UserFills>;
|
|
33
|
+
/**
|
|
34
|
+
* Get user's fill and cancel and other all orders history
|
|
35
|
+
*/
|
|
36
|
+
getUserHistoricalOrders(address?: string, startTime?: number, endTime?: number): Promise<UserHistoricalOrders[]>;
|
|
32
37
|
/**
|
|
33
38
|
* Get candlestick data snapshot
|
|
34
39
|
* Supported intervals: "1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "8h", "12h", "1d", "3d", "1w", "1M"
|
|
35
40
|
*/
|
|
36
|
-
candleSnapshot(coin: string, interval: string, startTime
|
|
41
|
+
candleSnapshot(coin: string, interval: string, startTime: number, endTime: number): Promise<CandleSnapshot>;
|
|
37
42
|
/**
|
|
38
|
-
* Check builder fee
|
|
43
|
+
* Check max builder fee
|
|
39
44
|
*/
|
|
40
|
-
|
|
45
|
+
getMaxBuilderFee(builder: string, address?: string): Promise<number>;
|
|
41
46
|
/**
|
|
42
47
|
* Get user role
|
|
43
48
|
*/
|
|
@@ -36,6 +36,9 @@ class InfoClient {
|
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
+
initMasterAddress(masterAddress) {
|
|
40
|
+
this.masterAddress = masterAddress;
|
|
41
|
+
}
|
|
39
42
|
/**
|
|
40
43
|
* Get metadata and asset contexts
|
|
41
44
|
*/
|
|
@@ -65,18 +68,26 @@ class InfoClient {
|
|
|
65
68
|
}
|
|
66
69
|
getClearingHouseState(address) {
|
|
67
70
|
return __awaiter(this, void 0, void 0, function* () {
|
|
71
|
+
const user = address || this.masterAddress;
|
|
72
|
+
if (!user) {
|
|
73
|
+
throw new Error('user address is empty');
|
|
74
|
+
}
|
|
68
75
|
return this.httpClient.info({
|
|
69
76
|
type: constants_1.InfoType.CLEARINGHOUSE_STATE,
|
|
70
|
-
user
|
|
77
|
+
user,
|
|
71
78
|
});
|
|
72
79
|
});
|
|
73
80
|
}
|
|
74
81
|
// frontendOpenOrders
|
|
75
82
|
getFrontendOpenOrders(address) {
|
|
76
83
|
return __awaiter(this, void 0, void 0, function* () {
|
|
84
|
+
const user = address || this.masterAddress;
|
|
85
|
+
if (!user) {
|
|
86
|
+
throw new Error('user address is empty');
|
|
87
|
+
}
|
|
77
88
|
return this.httpClient.info({
|
|
78
89
|
type: constants_1.InfoType.FRONTEND_OPEN_ORDERS,
|
|
79
|
-
user
|
|
90
|
+
user,
|
|
80
91
|
});
|
|
81
92
|
});
|
|
82
93
|
}
|
|
@@ -89,9 +100,13 @@ class InfoClient {
|
|
|
89
100
|
// }
|
|
90
101
|
getUserNonFundingLedgerUpdates(address, startTime, endTime) {
|
|
91
102
|
return __awaiter(this, void 0, void 0, function* () {
|
|
103
|
+
const user = address || this.masterAddress;
|
|
104
|
+
if (!user) {
|
|
105
|
+
throw new Error('user address is empty');
|
|
106
|
+
}
|
|
92
107
|
return this.httpClient.info({
|
|
93
108
|
type: constants_1.InfoType.USER_NON_FUNDING_LEDGER_UPDATES,
|
|
94
|
-
user
|
|
109
|
+
user,
|
|
95
110
|
startTime: startTime || 0,
|
|
96
111
|
endTime,
|
|
97
112
|
});
|
|
@@ -100,11 +115,33 @@ class InfoClient {
|
|
|
100
115
|
/**
|
|
101
116
|
* Get user's fill history
|
|
102
117
|
*/
|
|
103
|
-
getUserFills(address) {
|
|
118
|
+
getUserFills(address, aggregateByTime) {
|
|
104
119
|
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
const user = address || this.masterAddress;
|
|
121
|
+
if (!user) {
|
|
122
|
+
throw new Error('user address is empty');
|
|
123
|
+
}
|
|
105
124
|
return this.httpClient.info({
|
|
106
125
|
type: constants_1.InfoType.USER_FILLS,
|
|
107
|
-
user
|
|
126
|
+
user,
|
|
127
|
+
aggregateByTime: aggregateByTime || true,
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get user's fill and cancel and other all orders history
|
|
133
|
+
*/
|
|
134
|
+
getUserHistoricalOrders(address, startTime, endTime) {
|
|
135
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
const user = address || this.masterAddress;
|
|
137
|
+
if (!user) {
|
|
138
|
+
throw new Error('user address is empty');
|
|
139
|
+
}
|
|
140
|
+
return this.httpClient.info({
|
|
141
|
+
type: constants_1.InfoType.USER_HISTORICAL_ORDERS,
|
|
142
|
+
user,
|
|
143
|
+
startTime: startTime || 0,
|
|
144
|
+
endTime,
|
|
108
145
|
});
|
|
109
146
|
});
|
|
110
147
|
}
|
|
@@ -116,26 +153,29 @@ class InfoClient {
|
|
|
116
153
|
return __awaiter(this, void 0, void 0, function* () {
|
|
117
154
|
// if no time range is specified, default to 24 hours ago
|
|
118
155
|
const now = Date.now();
|
|
119
|
-
const defaultStartTime = now - 24 * 60 * 60 * 1000; // 24 hours ago
|
|
120
156
|
return this.httpClient.info({
|
|
121
157
|
type: constants_1.InfoType.CANDLES_SNAPSHOT,
|
|
122
158
|
req: {
|
|
123
159
|
coin,
|
|
124
160
|
interval,
|
|
125
|
-
startTime: startTime
|
|
126
|
-
endTime: endTime
|
|
161
|
+
startTime: startTime,
|
|
162
|
+
endTime: endTime !== null && endTime !== void 0 ? endTime : now,
|
|
127
163
|
},
|
|
128
164
|
});
|
|
129
165
|
});
|
|
130
166
|
}
|
|
131
167
|
/**
|
|
132
|
-
* Check builder fee
|
|
168
|
+
* Check max builder fee
|
|
133
169
|
*/
|
|
134
|
-
|
|
170
|
+
getMaxBuilderFee(builder, address) {
|
|
135
171
|
return __awaiter(this, void 0, void 0, function* () {
|
|
172
|
+
const user = address || this.masterAddress;
|
|
173
|
+
if (!user) {
|
|
174
|
+
throw new Error('user address is empty');
|
|
175
|
+
}
|
|
136
176
|
return this.httpClient.info({
|
|
137
|
-
type: constants_1.InfoType.
|
|
138
|
-
user
|
|
177
|
+
type: constants_1.InfoType.MAX_BUILDER_FEE,
|
|
178
|
+
user,
|
|
139
179
|
builder,
|
|
140
180
|
});
|
|
141
181
|
});
|
|
@@ -145,9 +185,13 @@ class InfoClient {
|
|
|
145
185
|
*/
|
|
146
186
|
getUserRole(address) {
|
|
147
187
|
return __awaiter(this, void 0, void 0, function* () {
|
|
188
|
+
const user = address || this.masterAddress;
|
|
189
|
+
if (!user) {
|
|
190
|
+
throw new Error('user address is empty');
|
|
191
|
+
}
|
|
148
192
|
const res = yield this.httpClient.info({
|
|
149
193
|
type: constants_1.InfoType.USER_ROLE,
|
|
150
|
-
user
|
|
194
|
+
user,
|
|
151
195
|
});
|
|
152
196
|
return res;
|
|
153
197
|
});
|
|
@@ -157,9 +201,13 @@ class InfoClient {
|
|
|
157
201
|
*/
|
|
158
202
|
getUsersFees(address) {
|
|
159
203
|
return __awaiter(this, void 0, void 0, function* () {
|
|
204
|
+
const user = address || this.masterAddress;
|
|
205
|
+
if (!user) {
|
|
206
|
+
throw new Error('user address is empty');
|
|
207
|
+
}
|
|
160
208
|
const res = yield this.httpClient.info({
|
|
161
209
|
type: constants_1.InfoType.USER_FEES,
|
|
162
|
-
user
|
|
210
|
+
user,
|
|
163
211
|
});
|
|
164
212
|
// const perpFee = Number(res.userCrossRate) * (1 - Number(res.activeReferralDiscount));
|
|
165
213
|
return res;
|
|
@@ -170,9 +218,13 @@ class InfoClient {
|
|
|
170
218
|
*/
|
|
171
219
|
extraAgents(address) {
|
|
172
220
|
return __awaiter(this, void 0, void 0, function* () {
|
|
221
|
+
const user = address || this.masterAddress;
|
|
222
|
+
if (!user) {
|
|
223
|
+
throw new Error('user address is empty');
|
|
224
|
+
}
|
|
173
225
|
return this.httpClient.info({
|
|
174
226
|
type: constants_1.InfoType.EXTRA_AGENTS,
|
|
175
|
-
user
|
|
227
|
+
user,
|
|
176
228
|
});
|
|
177
229
|
});
|
|
178
230
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { AllMids, L2Book, Candle,
|
|
1
|
+
import type { AllMids, L2Book, Candle, WsOrder, WebData2, WsActiveAssetCtx, WsUserFills } from '../types';
|
|
2
2
|
export interface WebSocketClientConfig {
|
|
3
|
-
masterAddress
|
|
3
|
+
masterAddress?: string;
|
|
4
4
|
isTestnet?: boolean;
|
|
5
5
|
autoReconnect?: boolean;
|
|
6
6
|
reconnectDelay?: number;
|
|
@@ -21,11 +21,22 @@ export declare class WebSocketClient {
|
|
|
21
21
|
private reconnectAttempts;
|
|
22
22
|
private isConnecting;
|
|
23
23
|
private pendingSubscriptions;
|
|
24
|
+
private heartbeatInterval;
|
|
25
|
+
private readonly HEARTBEAT_INTERVAL;
|
|
24
26
|
constructor(config: WebSocketClientConfig);
|
|
27
|
+
initMasterAddress(masterAddress: string): void;
|
|
25
28
|
/**
|
|
26
29
|
* Connect to the WebSocket
|
|
27
30
|
*/
|
|
28
31
|
connect(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Start heartbeat to keep connection alive
|
|
34
|
+
*/
|
|
35
|
+
private startHeartbeat;
|
|
36
|
+
/**
|
|
37
|
+
* Stop heartbeat
|
|
38
|
+
*/
|
|
39
|
+
private stopHeartbeat;
|
|
29
40
|
/**
|
|
30
41
|
* Disconnect from the WebSocket
|
|
31
42
|
*/
|
|
@@ -57,7 +68,7 @@ export declare class WebSocketClient {
|
|
|
57
68
|
/**
|
|
58
69
|
* Subscribe to user fills
|
|
59
70
|
*/
|
|
60
|
-
subscribeToUserFills(callback: SubscriptionCallback<
|
|
71
|
+
subscribeToUserFills(callback: SubscriptionCallback<WsUserFills>): Subscription;
|
|
61
72
|
/**
|
|
62
73
|
* Subscribe to user order updates
|
|
63
74
|
*/
|
|
@@ -21,9 +21,20 @@ class WebSocketClient {
|
|
|
21
21
|
this.reconnectAttempts = 0;
|
|
22
22
|
this.isConnecting = false;
|
|
23
23
|
this.pendingSubscriptions = [];
|
|
24
|
-
this.
|
|
24
|
+
this.heartbeatInterval = null;
|
|
25
|
+
this.HEARTBEAT_INTERVAL = 50000; // 50秒发送一次心跳,小于60秒超时
|
|
26
|
+
this.config = {
|
|
27
|
+
isTestnet: false,
|
|
28
|
+
autoReconnect: true,
|
|
29
|
+
reconnectDelay: 3000,
|
|
30
|
+
maxReconnectAttempts: 5,
|
|
31
|
+
masterAddress: config.masterAddress || '',
|
|
32
|
+
};
|
|
25
33
|
this.url = this.config.isTestnet ? constants_1.WSS_URLS.TESTNET : constants_1.WSS_URLS.PRODUCTION;
|
|
26
34
|
}
|
|
35
|
+
initMasterAddress(masterAddress) {
|
|
36
|
+
this.config.masterAddress = masterAddress;
|
|
37
|
+
}
|
|
27
38
|
/**
|
|
28
39
|
* Connect to the WebSocket
|
|
29
40
|
*/
|
|
@@ -57,6 +68,8 @@ class WebSocketClient {
|
|
|
57
68
|
this.isConnecting = false;
|
|
58
69
|
this.reconnectAttempts = 0;
|
|
59
70
|
console.log('hyperliquid sdk WebSocket connected');
|
|
71
|
+
// Start heartbeat
|
|
72
|
+
this.startHeartbeat();
|
|
60
73
|
// Resubscribe to pending subscriptions
|
|
61
74
|
this.pendingSubscriptions.forEach(({ message, callback }) => {
|
|
62
75
|
var _a;
|
|
@@ -71,6 +84,11 @@ class WebSocketClient {
|
|
|
71
84
|
this.ws.onmessage = (event) => {
|
|
72
85
|
try {
|
|
73
86
|
const data = JSON.parse(event.data);
|
|
87
|
+
// Handle pong response
|
|
88
|
+
if (data.channel === 'pong') {
|
|
89
|
+
// console.log('Received pong from server'); // 可以注释掉以减少日志输出
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
74
92
|
const type = data.channel;
|
|
75
93
|
const callback = this.subscriptions.get(type);
|
|
76
94
|
if (callback) {
|
|
@@ -84,6 +102,8 @@ class WebSocketClient {
|
|
|
84
102
|
this.ws.onclose = () => {
|
|
85
103
|
this.isConnecting = false;
|
|
86
104
|
console.log('WebSocket disconnected');
|
|
105
|
+
// Stop heartbeat
|
|
106
|
+
this.stopHeartbeat();
|
|
87
107
|
if (this.config.autoReconnect && this.reconnectAttempts < this.config.maxReconnectAttempts) {
|
|
88
108
|
this.reconnectAttempts++;
|
|
89
109
|
console.log(`Reconnecting... (${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`);
|
|
@@ -92,16 +112,39 @@ class WebSocketClient {
|
|
|
92
112
|
};
|
|
93
113
|
this.ws.onerror = (error) => {
|
|
94
114
|
this.isConnecting = false;
|
|
115
|
+
this.stopHeartbeat();
|
|
95
116
|
console.error('WebSocket error:', error);
|
|
96
117
|
reject(error);
|
|
97
118
|
};
|
|
98
119
|
});
|
|
99
120
|
});
|
|
100
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Start heartbeat to keep connection alive
|
|
124
|
+
*/
|
|
125
|
+
startHeartbeat() {
|
|
126
|
+
this.stopHeartbeat(); // Clear any existing heartbeat
|
|
127
|
+
this.heartbeatInterval = setInterval(() => {
|
|
128
|
+
var _a;
|
|
129
|
+
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
130
|
+
this.ws.send(JSON.stringify({ method: 'ping' }));
|
|
131
|
+
}
|
|
132
|
+
}, this.HEARTBEAT_INTERVAL);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Stop heartbeat
|
|
136
|
+
*/
|
|
137
|
+
stopHeartbeat() {
|
|
138
|
+
if (this.heartbeatInterval) {
|
|
139
|
+
clearInterval(this.heartbeatInterval);
|
|
140
|
+
this.heartbeatInterval = null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
101
143
|
/**
|
|
102
144
|
* Disconnect from the WebSocket
|
|
103
145
|
*/
|
|
104
146
|
disconnect() {
|
|
147
|
+
this.stopHeartbeat();
|
|
105
148
|
if (this.ws) {
|
|
106
149
|
this.ws.close();
|
|
107
150
|
this.ws = null;
|
|
@@ -182,14 +225,20 @@ class WebSocketClient {
|
|
|
182
225
|
* Subscribe to user fills
|
|
183
226
|
*/
|
|
184
227
|
subscribeToUserFills(callback) {
|
|
228
|
+
if (!this.config.masterAddress) {
|
|
229
|
+
throw new Error('masterAddress is empty');
|
|
230
|
+
}
|
|
185
231
|
return this.subscribe({
|
|
186
|
-
subscription: { type: 'userFills', user: this.config.masterAddress },
|
|
232
|
+
subscription: { type: 'userFills', user: this.config.masterAddress, aggregateByTime: true },
|
|
187
233
|
}, callback);
|
|
188
234
|
}
|
|
189
235
|
/**
|
|
190
236
|
* Subscribe to user order updates
|
|
191
237
|
*/
|
|
192
238
|
subscribeToUserOrders(callback) {
|
|
239
|
+
if (!this.config.masterAddress) {
|
|
240
|
+
throw new Error('masterAddress is empty');
|
|
241
|
+
}
|
|
193
242
|
return this.subscribe({
|
|
194
243
|
subscription: { type: 'orderUpdates', user: this.config.masterAddress },
|
|
195
244
|
}, callback);
|
|
@@ -198,6 +247,9 @@ class WebSocketClient {
|
|
|
198
247
|
* Subscribe to user funding updates
|
|
199
248
|
*/
|
|
200
249
|
subscribeToUserFunding(callback) {
|
|
250
|
+
if (!this.config.masterAddress) {
|
|
251
|
+
throw new Error('masterAddress is empty');
|
|
252
|
+
}
|
|
201
253
|
return this.subscribe({
|
|
202
254
|
subscription: { type: 'userFundings', user: this.config.masterAddress },
|
|
203
255
|
}, callback);
|
|
@@ -207,6 +259,9 @@ class WebSocketClient {
|
|
|
207
259
|
* Includes positions, margin summary, open orders, and other user data
|
|
208
260
|
*/
|
|
209
261
|
subscribeToWebData2(callback) {
|
|
262
|
+
if (!this.config.masterAddress) {
|
|
263
|
+
throw new Error('masterAddress is empty');
|
|
264
|
+
}
|
|
210
265
|
return this.subscribe({
|
|
211
266
|
subscription: { type: 'webData2', user: this.config.masterAddress },
|
|
212
267
|
}, callback);
|
|
@@ -4,7 +4,7 @@ import type { ClearinghouseState } from './types';
|
|
|
4
4
|
import { WebSocketClient, WebSocketClientConfig } from './client/websocket-client';
|
|
5
5
|
import { SymbolConversion } from './client/symbolConversion';
|
|
6
6
|
export interface HyperliquidSDKConfig {
|
|
7
|
-
masterAddress
|
|
7
|
+
masterAddress?: string;
|
|
8
8
|
agentPrivateKey?: string;
|
|
9
9
|
agentPublicKey?: string;
|
|
10
10
|
agentName?: string;
|
|
@@ -22,14 +22,14 @@ export declare class HyperliquidSDK {
|
|
|
22
22
|
exchange?: ExchangeClient;
|
|
23
23
|
symbolConversion: SymbolConversion;
|
|
24
24
|
readonly ws: WebSocketClient;
|
|
25
|
-
private readonly masterAddress
|
|
25
|
+
private readonly masterAddress?;
|
|
26
26
|
private readonly configParams;
|
|
27
27
|
constructor(config: HyperliquidSDKConfig);
|
|
28
28
|
/**
|
|
29
29
|
* Get wallet address (only available if private key was provided)
|
|
30
30
|
*/
|
|
31
31
|
get address(): string;
|
|
32
|
-
|
|
32
|
+
initAccount(masterAddress: string, agentPrivateKey: string, agentPublicKey: string, agentName?: string): void;
|
|
33
33
|
/**
|
|
34
34
|
* Connect to WebSocket for real-time data
|
|
35
35
|
*/
|
package/dist/hyperliquid-sdk.js
CHANGED
|
@@ -33,7 +33,7 @@ class HyperliquidSDK {
|
|
|
33
33
|
this.masterAddress = config.masterAddress;
|
|
34
34
|
this.configParams = config;
|
|
35
35
|
// Initialize exchange client only if private key is provided
|
|
36
|
-
if (config.
|
|
36
|
+
if (config.masterAddress) {
|
|
37
37
|
this.exchange = new exchange_client_1.ExchangeClient({
|
|
38
38
|
masterAddress: config.masterAddress,
|
|
39
39
|
agentPrivateKey: config.agentPrivateKey,
|
|
@@ -51,12 +51,25 @@ class HyperliquidSDK {
|
|
|
51
51
|
* Get wallet address (only available if private key was provided)
|
|
52
52
|
*/
|
|
53
53
|
get address() {
|
|
54
|
-
return this.masterAddress;
|
|
54
|
+
return this.masterAddress || '';
|
|
55
55
|
}
|
|
56
|
-
|
|
57
|
-
this.
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
initAccount(masterAddress, agentPrivateKey, agentPublicKey, agentName) {
|
|
57
|
+
this.info.initMasterAddress(masterAddress);
|
|
58
|
+
this.ws.initMasterAddress(masterAddress);
|
|
59
|
+
if (this.exchange) {
|
|
60
|
+
this.exchange.initAccount(masterAddress, agentPrivateKey, agentPublicKey, agentName);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.exchange = new exchange_client_1.ExchangeClient({
|
|
64
|
+
masterAddress,
|
|
65
|
+
symbolConversion: this.symbolConversion,
|
|
66
|
+
agentPrivateKey,
|
|
67
|
+
agentPublicKey,
|
|
68
|
+
agentName,
|
|
69
|
+
isTestnet: this.configParams.isTestnet,
|
|
70
|
+
timeout: this.configParams.timeout,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
60
73
|
}
|
|
61
74
|
/**
|
|
62
75
|
* Connect to WebSocket for real-time data
|
|
@@ -28,6 +28,7 @@ export declare enum ExchangeType {
|
|
|
28
28
|
export declare enum InfoType {
|
|
29
29
|
ALL_MIDS = "allMids",
|
|
30
30
|
USER_NON_FUNDING_LEDGER_UPDATES = "userNonFundingLedgerUpdates",
|
|
31
|
+
USER_HISTORICAL_ORDERS = "historicalOrders",
|
|
31
32
|
USER_OPEN_ORDERS = "openOrders",
|
|
32
33
|
USER_FILLS = "userFills",
|
|
33
34
|
USER_FILLS_BY_TIME = "userFillsByTime",
|
|
@@ -42,7 +43,7 @@ export declare enum InfoType {
|
|
|
42
43
|
FRONTEND_OPEN_ORDERS = "frontendOpenOrders",
|
|
43
44
|
PREDICTED_FUNDINGS = "predictedFundings",
|
|
44
45
|
ACTIVE_ASSET_DATA = "activeAssetData",
|
|
45
|
-
|
|
46
|
+
MAX_BUILDER_FEE = "maxBuilderFee",
|
|
46
47
|
USER_FEES = "userFees",
|
|
47
48
|
USER_ROLE = "userRole",
|
|
48
49
|
EXTRA_AGENTS = "extraAgents"
|
package/dist/types/constants.js
CHANGED
|
@@ -36,6 +36,7 @@ var InfoType;
|
|
|
36
36
|
(function (InfoType) {
|
|
37
37
|
InfoType["ALL_MIDS"] = "allMids";
|
|
38
38
|
InfoType["USER_NON_FUNDING_LEDGER_UPDATES"] = "userNonFundingLedgerUpdates";
|
|
39
|
+
InfoType["USER_HISTORICAL_ORDERS"] = "historicalOrders";
|
|
39
40
|
InfoType["USER_OPEN_ORDERS"] = "openOrders";
|
|
40
41
|
InfoType["USER_FILLS"] = "userFills";
|
|
41
42
|
InfoType["USER_FILLS_BY_TIME"] = "userFillsByTime";
|
|
@@ -50,7 +51,7 @@ var InfoType;
|
|
|
50
51
|
InfoType["FRONTEND_OPEN_ORDERS"] = "frontendOpenOrders";
|
|
51
52
|
InfoType["PREDICTED_FUNDINGS"] = "predictedFundings";
|
|
52
53
|
InfoType["ACTIVE_ASSET_DATA"] = "activeAssetData";
|
|
53
|
-
InfoType["
|
|
54
|
+
InfoType["MAX_BUILDER_FEE"] = "maxBuilderFee";
|
|
54
55
|
InfoType["USER_FEES"] = "userFees";
|
|
55
56
|
InfoType["USER_ROLE"] = "userRole";
|
|
56
57
|
InfoType["EXTRA_AGENTS"] = "extraAgents";
|
package/dist/types/index.d.ts
CHANGED
|
@@ -9,6 +9,28 @@ export interface MarketOverview {
|
|
|
9
9
|
assetContexts: AssetCtx[];
|
|
10
10
|
marginTables: [number, MarginTable][];
|
|
11
11
|
}
|
|
12
|
+
export interface UserHistoricalOrders {
|
|
13
|
+
order: {
|
|
14
|
+
coin: string;
|
|
15
|
+
side: Side;
|
|
16
|
+
limitPx: string;
|
|
17
|
+
sz: string;
|
|
18
|
+
oid: number;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
triggerCondition: string;
|
|
21
|
+
isTrigger: boolean;
|
|
22
|
+
triggerPx: string;
|
|
23
|
+
children: any;
|
|
24
|
+
isPositionTpsl: boolean;
|
|
25
|
+
reduceOnly: boolean;
|
|
26
|
+
orderType: string;
|
|
27
|
+
origSz: string;
|
|
28
|
+
tif: string;
|
|
29
|
+
cloid: string | null;
|
|
30
|
+
};
|
|
31
|
+
status: string;
|
|
32
|
+
statusTimestamp: number;
|
|
33
|
+
}
|
|
12
34
|
export interface OpenOrder {
|
|
13
35
|
coin: string;
|
|
14
36
|
side: Side;
|
|
@@ -210,6 +232,11 @@ export interface WsOrder {
|
|
|
210
232
|
children?: WsOrder[];
|
|
211
233
|
user: string;
|
|
212
234
|
}
|
|
235
|
+
export interface WsUserFills {
|
|
236
|
+
isSnapshot?: boolean;
|
|
237
|
+
user: string;
|
|
238
|
+
fills: WsFill[];
|
|
239
|
+
}
|
|
213
240
|
export interface WsFill {
|
|
214
241
|
coin: string;
|
|
215
242
|
px: string;
|
|
@@ -224,6 +251,11 @@ export interface WsFill {
|
|
|
224
251
|
crossed: boolean;
|
|
225
252
|
fee: string;
|
|
226
253
|
tid: number;
|
|
254
|
+
liquidation?: {
|
|
255
|
+
liquidatedUser: string;
|
|
256
|
+
markPx: string;
|
|
257
|
+
method: string;
|
|
258
|
+
};
|
|
227
259
|
}
|
|
228
260
|
export interface AssetCtx {
|
|
229
261
|
dayBaseVlm: string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes trailing zeros from a string representation of a number.
|
|
3
|
+
* This is useful when working with price and size fields directly.
|
|
4
|
+
*
|
|
5
|
+
* Hyperliquid API requires that price ('p') and size ('s') fields do not contain trailing zeros.
|
|
6
|
+
* For example, "12345.0" should be "12345" and "0.123450" should be "0.12345".
|
|
7
|
+
* This function ensures that all numeric string values are properly formatted.
|
|
8
|
+
*
|
|
9
|
+
* @param value - The string value to normalize
|
|
10
|
+
* @returns The normalized string without trailing zeros
|
|
11
|
+
*/
|
|
12
|
+
export declare function removeTrailingZeros(value: string): string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeTrailingZeros = removeTrailingZeros;
|
|
4
|
+
/**
|
|
5
|
+
* Removes trailing zeros from a string representation of a number.
|
|
6
|
+
* This is useful when working with price and size fields directly.
|
|
7
|
+
*
|
|
8
|
+
* Hyperliquid API requires that price ('p') and size ('s') fields do not contain trailing zeros.
|
|
9
|
+
* For example, "12345.0" should be "12345" and "0.123450" should be "0.12345".
|
|
10
|
+
* This function ensures that all numeric string values are properly formatted.
|
|
11
|
+
*
|
|
12
|
+
* @param value - The string value to normalize
|
|
13
|
+
* @returns The normalized string without trailing zeros
|
|
14
|
+
*/
|
|
15
|
+
function removeTrailingZeros(value) {
|
|
16
|
+
if (!value.includes('.'))
|
|
17
|
+
return value;
|
|
18
|
+
const normalized = value.replace(/\.?0+$/, '');
|
|
19
|
+
if (normalized === '-0')
|
|
20
|
+
return '0';
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
package/package.json
CHANGED