@rabby-wallet/hyperliquid-sdk 1.0.0-beta.9 → 1.0.2
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 +24 -3
- package/dist/client/websocket-client.js +107 -9
- 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;
|
|
@@ -18,14 +18,27 @@ export declare class WebSocketClient {
|
|
|
18
18
|
private readonly url;
|
|
19
19
|
private readonly config;
|
|
20
20
|
private subscriptions;
|
|
21
|
+
private activeSubscriptions;
|
|
21
22
|
private reconnectAttempts;
|
|
22
23
|
private isConnecting;
|
|
24
|
+
private isManualDisconnect;
|
|
23
25
|
private pendingSubscriptions;
|
|
26
|
+
private heartbeatInterval;
|
|
27
|
+
private readonly HEARTBEAT_INTERVAL;
|
|
24
28
|
constructor(config: WebSocketClientConfig);
|
|
29
|
+
initMasterAddress(masterAddress: string): void;
|
|
25
30
|
/**
|
|
26
31
|
* Connect to the WebSocket
|
|
27
32
|
*/
|
|
28
33
|
connect(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Start heartbeat to keep connection alive
|
|
36
|
+
*/
|
|
37
|
+
private startHeartbeat;
|
|
38
|
+
/**
|
|
39
|
+
* Stop heartbeat
|
|
40
|
+
*/
|
|
41
|
+
private stopHeartbeat;
|
|
29
42
|
/**
|
|
30
43
|
* Disconnect from the WebSocket
|
|
31
44
|
*/
|
|
@@ -57,7 +70,7 @@ export declare class WebSocketClient {
|
|
|
57
70
|
/**
|
|
58
71
|
* Subscribe to user fills
|
|
59
72
|
*/
|
|
60
|
-
subscribeToUserFills(callback: SubscriptionCallback<
|
|
73
|
+
subscribeToUserFills(callback: SubscriptionCallback<WsUserFills>): Subscription;
|
|
61
74
|
/**
|
|
62
75
|
* Subscribe to user order updates
|
|
63
76
|
*/
|
|
@@ -79,4 +92,12 @@ export declare class WebSocketClient {
|
|
|
79
92
|
* Get current connection state
|
|
80
93
|
*/
|
|
81
94
|
get readyState(): number;
|
|
95
|
+
/**
|
|
96
|
+
* Get list of active subscription types
|
|
97
|
+
*/
|
|
98
|
+
getActiveSubscriptions(): string[];
|
|
99
|
+
/**
|
|
100
|
+
* Reset reconnection attempts and enable auto-reconnect
|
|
101
|
+
*/
|
|
102
|
+
resetReconnectState(): void;
|
|
82
103
|
}
|
|
@@ -18,12 +18,25 @@ class WebSocketClient {
|
|
|
18
18
|
constructor(config) {
|
|
19
19
|
this.ws = null;
|
|
20
20
|
this.subscriptions = new Map();
|
|
21
|
+
this.activeSubscriptions = new Map();
|
|
21
22
|
this.reconnectAttempts = 0;
|
|
22
23
|
this.isConnecting = false;
|
|
24
|
+
this.isManualDisconnect = false; // 标记是否为主动断开连接
|
|
23
25
|
this.pendingSubscriptions = [];
|
|
24
|
-
this.
|
|
26
|
+
this.heartbeatInterval = null;
|
|
27
|
+
this.HEARTBEAT_INTERVAL = 50000; // 50秒发送一次心跳,小于60秒超时
|
|
28
|
+
this.config = {
|
|
29
|
+
isTestnet: false,
|
|
30
|
+
autoReconnect: true,
|
|
31
|
+
reconnectDelay: 3000,
|
|
32
|
+
maxReconnectAttempts: 5,
|
|
33
|
+
masterAddress: config.masterAddress || '',
|
|
34
|
+
};
|
|
25
35
|
this.url = this.config.isTestnet ? constants_1.WSS_URLS.TESTNET : constants_1.WSS_URLS.PRODUCTION;
|
|
26
36
|
}
|
|
37
|
+
initMasterAddress(masterAddress) {
|
|
38
|
+
this.config.masterAddress = masterAddress;
|
|
39
|
+
}
|
|
27
40
|
/**
|
|
28
41
|
* Connect to the WebSocket
|
|
29
42
|
*/
|
|
@@ -56,21 +69,31 @@ class WebSocketClient {
|
|
|
56
69
|
this.ws.onopen = () => {
|
|
57
70
|
this.isConnecting = false;
|
|
58
71
|
this.reconnectAttempts = 0;
|
|
72
|
+
this.isManualDisconnect = false; // 重置手动断开标志
|
|
59
73
|
console.log('hyperliquid sdk WebSocket connected');
|
|
74
|
+
// Start heartbeat
|
|
75
|
+
this.startHeartbeat();
|
|
60
76
|
// Resubscribe to pending subscriptions
|
|
61
77
|
this.pendingSubscriptions.forEach(({ message, callback }) => {
|
|
62
78
|
var _a;
|
|
63
79
|
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
64
80
|
this.subscriptions.set(message.subscription.type, callback);
|
|
81
|
+
this.activeSubscriptions.set(message.subscription.type, { message, callback });
|
|
65
82
|
this.ws.send(JSON.stringify(Object.assign(Object.assign({}, message), { method: 'subscribe' })));
|
|
66
83
|
}
|
|
67
84
|
});
|
|
68
85
|
this.pendingSubscriptions = [];
|
|
86
|
+
console.log(`Resubscribed to ${this.activeSubscriptions.size} channels after reconnection`);
|
|
69
87
|
resolve();
|
|
70
88
|
};
|
|
71
89
|
this.ws.onmessage = (event) => {
|
|
72
90
|
try {
|
|
73
91
|
const data = JSON.parse(event.data);
|
|
92
|
+
// Handle pong response
|
|
93
|
+
if (data.channel === 'pong') {
|
|
94
|
+
// console.log('Received pong from server'); // 可以注释掉以减少日志输出
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
74
97
|
const type = data.channel;
|
|
75
98
|
const callback = this.subscriptions.get(type);
|
|
76
99
|
if (callback) {
|
|
@@ -84,43 +107,91 @@ class WebSocketClient {
|
|
|
84
107
|
this.ws.onclose = () => {
|
|
85
108
|
this.isConnecting = false;
|
|
86
109
|
console.log('WebSocket disconnected');
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
110
|
+
// Stop heartbeat
|
|
111
|
+
this.stopHeartbeat();
|
|
112
|
+
// Only attempt reconnection if it's not a manual disconnect
|
|
113
|
+
if (!this.isManualDisconnect && this.config.autoReconnect) {
|
|
114
|
+
// Save current active subscriptions to pending for reconnection
|
|
115
|
+
if (this.activeSubscriptions.size > 0) {
|
|
116
|
+
this.activeSubscriptions.forEach((subscription, type) => {
|
|
117
|
+
const existingPending = this.pendingSubscriptions.find(sub => sub.message.subscription.type === type);
|
|
118
|
+
if (!existingPending) {
|
|
119
|
+
this.pendingSubscriptions.push(subscription);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
|
|
124
|
+
this.reconnectAttempts++;
|
|
125
|
+
console.log(`Reconnecting... (${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`);
|
|
126
|
+
setTimeout(() => this.connect(), this.config.reconnectDelay);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
console.error('Max reconnection attempts reached. Connection failed.');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else if (this.isManualDisconnect) {
|
|
133
|
+
this.isManualDisconnect = false; // Reset flag
|
|
91
134
|
}
|
|
92
135
|
};
|
|
93
136
|
this.ws.onerror = (error) => {
|
|
94
137
|
this.isConnecting = false;
|
|
138
|
+
this.stopHeartbeat();
|
|
95
139
|
console.error('WebSocket error:', error);
|
|
96
140
|
reject(error);
|
|
97
141
|
};
|
|
98
142
|
});
|
|
99
143
|
});
|
|
100
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Start heartbeat to keep connection alive
|
|
147
|
+
*/
|
|
148
|
+
startHeartbeat() {
|
|
149
|
+
this.stopHeartbeat(); // Clear any existing heartbeat
|
|
150
|
+
this.heartbeatInterval = setInterval(() => {
|
|
151
|
+
var _a;
|
|
152
|
+
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
153
|
+
this.ws.send(JSON.stringify({ method: 'ping' }));
|
|
154
|
+
}
|
|
155
|
+
}, this.HEARTBEAT_INTERVAL);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Stop heartbeat
|
|
159
|
+
*/
|
|
160
|
+
stopHeartbeat() {
|
|
161
|
+
if (this.heartbeatInterval) {
|
|
162
|
+
clearInterval(this.heartbeatInterval);
|
|
163
|
+
this.heartbeatInterval = null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
101
166
|
/**
|
|
102
167
|
* Disconnect from the WebSocket
|
|
103
168
|
*/
|
|
104
169
|
disconnect() {
|
|
170
|
+
this.isManualDisconnect = true; // 标记为主动断开
|
|
171
|
+
this.stopHeartbeat();
|
|
105
172
|
if (this.ws) {
|
|
106
173
|
this.ws.close();
|
|
107
174
|
this.ws = null;
|
|
108
175
|
}
|
|
109
176
|
this.subscriptions.clear();
|
|
177
|
+
this.activeSubscriptions.clear();
|
|
110
178
|
this.pendingSubscriptions = [];
|
|
179
|
+
this.reconnectAttempts = 0; // 重置重连计数
|
|
111
180
|
}
|
|
112
181
|
/**
|
|
113
182
|
* Subscribe to a channel
|
|
114
183
|
*/
|
|
115
184
|
subscribe(message, callback) {
|
|
116
185
|
var _a;
|
|
186
|
+
const subscriptionData = { message, callback };
|
|
117
187
|
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
118
188
|
this.subscriptions.set(message.subscription.type, callback);
|
|
189
|
+
this.activeSubscriptions.set(message.subscription.type, subscriptionData);
|
|
119
190
|
this.ws.send(JSON.stringify(Object.assign(Object.assign({}, message), { method: 'subscribe' })));
|
|
120
191
|
}
|
|
121
192
|
else {
|
|
122
193
|
// Store pending subscription for when connection is established
|
|
123
|
-
this.pendingSubscriptions.push(
|
|
194
|
+
this.pendingSubscriptions.push(subscriptionData);
|
|
124
195
|
// Auto-connect if not connected
|
|
125
196
|
if (!this.isConnecting && (!this.ws || this.ws.readyState === WebSocket.CLOSED)) {
|
|
126
197
|
this.connect().catch(console.error);
|
|
@@ -129,12 +200,14 @@ class WebSocketClient {
|
|
|
129
200
|
return {
|
|
130
201
|
unsubscribe: () => {
|
|
131
202
|
var _a;
|
|
132
|
-
|
|
203
|
+
const type = message.subscription.type;
|
|
204
|
+
this.subscriptions.delete(type);
|
|
205
|
+
this.activeSubscriptions.delete(type);
|
|
133
206
|
if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
|
|
134
207
|
this.ws.send(JSON.stringify(Object.assign(Object.assign({}, message), { method: 'unsubscribe' })));
|
|
135
208
|
}
|
|
136
209
|
// Remove from pending subscriptions
|
|
137
|
-
this.pendingSubscriptions = this.pendingSubscriptions.filter(sub => sub.message.subscription.type !==
|
|
210
|
+
this.pendingSubscriptions = this.pendingSubscriptions.filter(sub => sub.message.subscription.type !== type);
|
|
138
211
|
},
|
|
139
212
|
};
|
|
140
213
|
}
|
|
@@ -182,14 +255,20 @@ class WebSocketClient {
|
|
|
182
255
|
* Subscribe to user fills
|
|
183
256
|
*/
|
|
184
257
|
subscribeToUserFills(callback) {
|
|
258
|
+
if (!this.config.masterAddress) {
|
|
259
|
+
throw new Error('masterAddress is empty');
|
|
260
|
+
}
|
|
185
261
|
return this.subscribe({
|
|
186
|
-
subscription: { type: 'userFills', user: this.config.masterAddress },
|
|
262
|
+
subscription: { type: 'userFills', user: this.config.masterAddress, aggregateByTime: true },
|
|
187
263
|
}, callback);
|
|
188
264
|
}
|
|
189
265
|
/**
|
|
190
266
|
* Subscribe to user order updates
|
|
191
267
|
*/
|
|
192
268
|
subscribeToUserOrders(callback) {
|
|
269
|
+
if (!this.config.masterAddress) {
|
|
270
|
+
throw new Error('masterAddress is empty');
|
|
271
|
+
}
|
|
193
272
|
return this.subscribe({
|
|
194
273
|
subscription: { type: 'orderUpdates', user: this.config.masterAddress },
|
|
195
274
|
}, callback);
|
|
@@ -198,6 +277,9 @@ class WebSocketClient {
|
|
|
198
277
|
* Subscribe to user funding updates
|
|
199
278
|
*/
|
|
200
279
|
subscribeToUserFunding(callback) {
|
|
280
|
+
if (!this.config.masterAddress) {
|
|
281
|
+
throw new Error('masterAddress is empty');
|
|
282
|
+
}
|
|
201
283
|
return this.subscribe({
|
|
202
284
|
subscription: { type: 'userFundings', user: this.config.masterAddress },
|
|
203
285
|
}, callback);
|
|
@@ -207,6 +289,9 @@ class WebSocketClient {
|
|
|
207
289
|
* Includes positions, margin summary, open orders, and other user data
|
|
208
290
|
*/
|
|
209
291
|
subscribeToWebData2(callback) {
|
|
292
|
+
if (!this.config.masterAddress) {
|
|
293
|
+
throw new Error('masterAddress is empty');
|
|
294
|
+
}
|
|
210
295
|
return this.subscribe({
|
|
211
296
|
subscription: { type: 'webData2', user: this.config.masterAddress },
|
|
212
297
|
}, callback);
|
|
@@ -225,5 +310,18 @@ class WebSocketClient {
|
|
|
225
310
|
var _a, _b;
|
|
226
311
|
return (_b = (_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) !== null && _b !== void 0 ? _b : WebSocket.CLOSED;
|
|
227
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Get list of active subscription types
|
|
315
|
+
*/
|
|
316
|
+
getActiveSubscriptions() {
|
|
317
|
+
return Array.from(this.activeSubscriptions.keys());
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Reset reconnection attempts and enable auto-reconnect
|
|
321
|
+
*/
|
|
322
|
+
resetReconnectState() {
|
|
323
|
+
this.reconnectAttempts = 0;
|
|
324
|
+
this.isManualDisconnect = false;
|
|
325
|
+
}
|
|
228
326
|
}
|
|
229
327
|
exports.WebSocketClient = WebSocketClient;
|
|
@@ -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