@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.
@@ -9,10 +9,11 @@ export declare class ExchangeClient {
9
9
  private agentPublicKey?;
10
10
  private agentName?;
11
11
  private readonly isTestnet;
12
- private readonly masterAddress;
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' : '0x42161',
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: 'signatureChainId', type: 'string' },
483
- { name: 'builder', type: 'string' },
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' : '0x42161',
526
+ signatureChainId: this.isTestnet ? '0x66eee' : '0xa4b1',
522
527
  destination: params.destination,
523
528
  amount: params.amount,
524
- time: Date.now(),
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: string;
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 readonly masterAddress;
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?: number, endTime?: number): Promise<CandleSnapshot>;
41
+ candleSnapshot(coin: string, interval: string, startTime: number, endTime: number): Promise<CandleSnapshot>;
37
42
  /**
38
- * Check builder fee approval status
43
+ * Check max builder fee
39
44
  */
40
- checkBuilderFeeApproval(builder: string, address?: string): Promise<any>;
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: address || this.masterAddress,
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: address || this.masterAddress,
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: address || this.masterAddress,
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: address || this.masterAddress,
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 || defaultStartTime,
126
- endTime: endTime || now,
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 approval status
168
+ * Check max builder fee
133
169
  */
134
- checkBuilderFeeApproval(builder, address) {
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.CHECK_BUILDER_FEE_APPROVAL,
138
- user: address || this.masterAddress,
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: address || this.masterAddress,
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: address || this.masterAddress,
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: address || this.masterAddress,
227
+ user,
176
228
  });
177
229
  });
178
230
  }
@@ -1,6 +1,6 @@
1
- import type { AllMids, L2Book, Candle, WsFill, WsOrder, WebData2, WsActiveAssetCtx } from '../types';
1
+ import type { AllMids, L2Book, Candle, WsOrder, WebData2, WsActiveAssetCtx, WsUserFills } from '../types';
2
2
  export interface WebSocketClientConfig {
3
- masterAddress: string;
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<WsFill[]>): Subscription;
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.config = Object.assign({ isTestnet: false, autoReconnect: true, reconnectDelay: 3000, maxReconnectAttempts: 5 }, config);
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: string;
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
- updateExchangeAgent(agentPrivateKey: string, agentPublicKey: string, agentName?: string): void;
32
+ initAccount(masterAddress: string, agentPrivateKey: string, agentPublicKey: string, agentName?: string): void;
33
33
  /**
34
34
  * Connect to WebSocket for real-time data
35
35
  */
@@ -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.agentPrivateKey) {
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
- updateExchangeAgent(agentPrivateKey, agentPublicKey, agentName) {
57
- this.exchange = new exchange_client_1.ExchangeClient(Object.assign(Object.assign({}, this.configParams), { symbolConversion: this.symbolConversion, agentPrivateKey,
58
- agentPublicKey,
59
- agentName }));
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
- CHECK_BUILDER_FEE_APPROVAL = "checkBuilderFeeApproval",
46
+ MAX_BUILDER_FEE = "maxBuilderFee",
46
47
  USER_FEES = "userFees",
47
48
  USER_ROLE = "userRole",
48
49
  EXTRA_AGENTS = "extraAgents"
@@ -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["CHECK_BUILDER_FEE_APPROVAL"] = "checkBuilderFeeApproval";
54
+ InfoType["MAX_BUILDER_FEE"] = "maxBuilderFee";
54
55
  InfoType["USER_FEES"] = "userFees";
55
56
  InfoType["USER_ROLE"] = "userRole";
56
57
  InfoType["EXTRA_AGENTS"] = "extraAgents";
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rabby-wallet/hyperliquid-sdk",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.1",
4
4
  "description": "Simplified Hyperliquid Perpetuals Trading SDK for Frontend Applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",