@rabby-wallet/hyperliquid-sdk 1.0.0-beta.2 → 1.0.0-beta.20

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/README.md CHANGED
@@ -32,8 +32,10 @@ const sdk = new HyperliquidSDK({
32
32
  // 获取所有中间价
33
33
  const prices = await sdk.info.getAllMids();
34
34
 
35
- // 获取市场元数据与资产上下文
36
- const [meta, assetCtxs] = await sdk.info.metaAndAssetCtxs();
35
+ // 获取市场元数据与资产上下文
36
+ const canUseCache = true; //默认使用缓存,多次请求并发去重 ,需要刷新传false
37
+ const [meta, assetCtxs] = await sdk.info.metaAndAssetCtxs(canUseCache);
38
+
37
39
 
38
40
  // 获取账户综合状态
39
41
  const account = await sdk.info.getClearingHouseState();
@@ -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
  */
@@ -32,7 +33,7 @@ export declare class ExchangeClient {
32
33
  * need to update leverage before place order
33
34
  */
34
35
  marketOrderOpen(params: MarketOrderParams): Promise<OrderResponse>;
35
- marketOrderClose(params: MarketOrderParams): Promise<OrderResponse>;
36
+ marketOrderClose(params: Omit<MarketOrderParams, 'tpTriggerPx' | 'slTriggerPx'>): Promise<OrderResponse>;
36
37
  /**
37
38
  * Place multiple orders at once
38
39
  */
@@ -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',
@@ -502,10 +507,11 @@ class ExchangeClient {
502
507
  */
503
508
  sendApproveBuilderFee(params) {
504
509
  return __awaiter(this, void 0, void 0, function* () {
510
+ const splitSignature = (0, signer_1.splitSig)(params.signature);
505
511
  return this.httpClient.exchange({
506
512
  action: params.action,
507
513
  nonce: params.nonce,
508
- signature: params.signature,
514
+ signature: splitSignature,
509
515
  });
510
516
  });
511
517
  }
@@ -517,17 +523,15 @@ class ExchangeClient {
517
523
  const action = {
518
524
  type: constants_1.ExchangeType.WITHDRAW,
519
525
  hyperliquidChain: this.isTestnet ? 'Testnet' : 'Mainnet',
520
- signatureChainId: this.isTestnet ? '0x66eee' : '0x42161',
526
+ signatureChainId: this.isTestnet ? '0x66eee' : '0xa4b1',
521
527
  destination: params.destination,
522
528
  amount: params.amount,
523
- time: Date.now(),
529
+ time: nonce,
524
530
  };
525
531
  const { domain, types, primaryType, message } = (0, signer_1.prepareMasterSignData)({
526
532
  action,
527
533
  payloadTypes: [
528
- { name: 'type', type: 'string' },
529
534
  { name: 'hyperliquidChain', type: 'string' },
530
- { name: 'signatureChainId', type: 'string' },
531
535
  { name: 'destination', type: 'string' },
532
536
  { name: 'amount', type: 'string' },
533
537
  { name: 'time', type: 'uint64' },
@@ -544,10 +548,11 @@ class ExchangeClient {
544
548
  };
545
549
  }
546
550
  sendWithdraw(params) {
551
+ const splitSignature = (0, signer_1.splitSig)(params.signature);
547
552
  return this.httpClient.exchange({
548
553
  action: params.action,
549
554
  nonce: params.nonce,
550
- signature: params.signature,
555
+ signature: splitSignature,
551
556
  });
552
557
  }
553
558
  }
@@ -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 } 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,31 +10,39 @@ export interface InfoClientConfig {
10
10
  */
11
11
  export declare class InfoClient {
12
12
  private readonly httpClient;
13
- private readonly masterAddress;
13
+ private masterAddress?;
14
+ private metaAndAssetCtxsCache;
15
+ private metaAndAssetCtxsInFlight;
14
16
  constructor(config: InfoClientConfig);
15
17
  /**
16
18
  * Get all mid prices for all assets
17
19
  */
18
20
  getAllMids(): Promise<AllMids>;
21
+ initMasterAddress(masterAddress: string): void;
19
22
  /**
20
23
  * Get metadata and asset contexts
21
24
  */
22
- metaAndAssetCtxs(): Promise<[Meta, AssetCtx[]]>;
25
+ metaAndAssetCtxs(canUseCache?: boolean): Promise<[Meta, AssetCtx[]]>;
23
26
  getClearingHouseState(address?: string): Promise<ClearinghouseState>;
24
27
  getFrontendOpenOrders(address?: string): Promise<OpenOrder[]>;
28
+ getUserNonFundingLedgerUpdates(address?: string, startTime?: number, endTime?: number): Promise<UserNonFundingLedgerUpdates[]>;
25
29
  /**
26
30
  * Get user's fill history
27
31
  */
28
- 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[]>;
29
37
  /**
30
38
  * Get candlestick data snapshot
31
39
  * Supported intervals: "1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "8h", "12h", "1d", "3d", "1w", "1M"
32
40
  */
33
- candleSnapshot(coin: string, interval: string, startTime?: number, endTime?: number): Promise<CandleSnapshot>;
41
+ candleSnapshot(coin: string, interval: string, startTime: number, endTime: number): Promise<CandleSnapshot>;
34
42
  /**
35
- * Check builder fee approval status
43
+ * Check max builder fee
36
44
  */
37
- checkBuilderFeeApproval(builder: string, address?: string): Promise<any>;
45
+ getMaxBuilderFee(builder: string, address?: string): Promise<number>;
38
46
  /**
39
47
  * Get user role
40
48
  */
@@ -18,6 +18,8 @@ const constants_1 = require("../types/constants");
18
18
  */
19
19
  class InfoClient {
20
20
  constructor(config) {
21
+ this.metaAndAssetCtxsCache = null;
22
+ this.metaAndAssetCtxsInFlight = null;
21
23
  this.masterAddress = config.masterAddress;
22
24
  this.httpClient = new http_client_1.HttpClient({
23
25
  isTestnet: config.isTestnet,
@@ -34,30 +36,58 @@ class InfoClient {
34
36
  });
35
37
  });
36
38
  }
39
+ initMasterAddress(masterAddress) {
40
+ this.masterAddress = masterAddress;
41
+ }
37
42
  /**
38
43
  * Get metadata and asset contexts
39
44
  */
40
45
  metaAndAssetCtxs() {
41
- return __awaiter(this, void 0, void 0, function* () {
42
- return this.httpClient.info({
43
- type: constants_1.InfoType.META_AND_ASSET_CTXS,
44
- });
46
+ return __awaiter(this, arguments, void 0, function* (canUseCache = true) {
47
+ if (canUseCache && this.metaAndAssetCtxsCache) {
48
+ return this.metaAndAssetCtxsCache;
49
+ }
50
+ // De-duplicate concurrent requests
51
+ if (this.metaAndAssetCtxsInFlight) {
52
+ return this.metaAndAssetCtxsInFlight;
53
+ }
54
+ this.metaAndAssetCtxsInFlight = (() => __awaiter(this, void 0, void 0, function* () {
55
+ try {
56
+ const [meta, assetCtxs] = yield this.httpClient.info({
57
+ type: constants_1.InfoType.META_AND_ASSET_CTXS,
58
+ });
59
+ this.metaAndAssetCtxsCache = [meta, assetCtxs];
60
+ return [meta, assetCtxs];
61
+ }
62
+ finally {
63
+ this.metaAndAssetCtxsInFlight = null;
64
+ }
65
+ }))();
66
+ return this.metaAndAssetCtxsInFlight;
45
67
  });
46
68
  }
47
69
  getClearingHouseState(address) {
48
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
+ }
49
75
  return this.httpClient.info({
50
76
  type: constants_1.InfoType.CLEARINGHOUSE_STATE,
51
- user: address || this.masterAddress,
77
+ user,
52
78
  });
53
79
  });
54
80
  }
55
81
  // frontendOpenOrders
56
82
  getFrontendOpenOrders(address) {
57
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
+ }
58
88
  return this.httpClient.info({
59
89
  type: constants_1.InfoType.FRONTEND_OPEN_ORDERS,
60
- user: address || this.masterAddress,
90
+ user,
61
91
  });
62
92
  });
63
93
  }
@@ -68,14 +98,50 @@ class InfoClient {
68
98
  // user: this.masterAddress,
69
99
  // });
70
100
  // }
101
+ getUserNonFundingLedgerUpdates(address, startTime, endTime) {
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
+ }
107
+ return this.httpClient.info({
108
+ type: constants_1.InfoType.USER_NON_FUNDING_LEDGER_UPDATES,
109
+ user,
110
+ startTime: startTime || 0,
111
+ endTime,
112
+ });
113
+ });
114
+ }
71
115
  /**
72
116
  * Get user's fill history
73
117
  */
74
- getUserFills(address) {
118
+ getUserFills(address, aggregateByTime) {
75
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
+ }
76
124
  return this.httpClient.info({
77
125
  type: constants_1.InfoType.USER_FILLS,
78
- 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,
79
145
  });
80
146
  });
81
147
  }
@@ -87,26 +153,29 @@ class InfoClient {
87
153
  return __awaiter(this, void 0, void 0, function* () {
88
154
  // if no time range is specified, default to 24 hours ago
89
155
  const now = Date.now();
90
- const defaultStartTime = now - 24 * 60 * 60 * 1000; // 24 hours ago
91
156
  return this.httpClient.info({
92
157
  type: constants_1.InfoType.CANDLES_SNAPSHOT,
93
158
  req: {
94
159
  coin,
95
160
  interval,
96
- startTime: startTime || defaultStartTime,
97
- endTime: endTime || now,
161
+ startTime: startTime,
162
+ endTime: endTime !== null && endTime !== void 0 ? endTime : now,
98
163
  },
99
164
  });
100
165
  });
101
166
  }
102
167
  /**
103
- * Check builder fee approval status
168
+ * Check max builder fee
104
169
  */
105
- checkBuilderFeeApproval(builder, address) {
170
+ getMaxBuilderFee(builder, address) {
106
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
+ }
107
176
  return this.httpClient.info({
108
- type: constants_1.InfoType.CHECK_BUILDER_FEE_APPROVAL,
109
- user: address || this.masterAddress,
177
+ type: constants_1.InfoType.MAX_BUILDER_FEE,
178
+ user,
110
179
  builder,
111
180
  });
112
181
  });
@@ -116,9 +185,13 @@ class InfoClient {
116
185
  */
117
186
  getUserRole(address) {
118
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
+ }
119
192
  const res = yield this.httpClient.info({
120
193
  type: constants_1.InfoType.USER_ROLE,
121
- user: address || this.masterAddress,
194
+ user,
122
195
  });
123
196
  return res;
124
197
  });
@@ -128,9 +201,13 @@ class InfoClient {
128
201
  */
129
202
  getUsersFees(address) {
130
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
+ }
131
208
  const res = yield this.httpClient.info({
132
209
  type: constants_1.InfoType.USER_FEES,
133
- user: address || this.masterAddress,
210
+ user,
134
211
  });
135
212
  // const perpFee = Number(res.userCrossRate) * (1 - Number(res.activeReferralDiscount));
136
213
  return res;
@@ -141,9 +218,13 @@ class InfoClient {
141
218
  */
142
219
  extraAgents(address) {
143
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
+ }
144
225
  return this.httpClient.info({
145
226
  type: constants_1.InfoType.EXTRA_AGENTS,
146
- user: address || this.masterAddress,
227
+ user,
147
228
  });
148
229
  });
149
230
  }
@@ -1,9 +1,11 @@
1
1
  import { HttpClientConfig } from "./http-client";
2
+ import { InfoClient } from "./info-client";
2
3
  export declare class SymbolConversion {
3
4
  private assetToIndexMap;
4
5
  private httpClient;
5
6
  private initialized;
6
- constructor(config: HttpClientConfig);
7
+ private infoClient?;
8
+ constructor(config: HttpClientConfig, infoClient?: InfoClient);
7
9
  initialize(): Promise<void>;
8
10
  private ensureInitialized;
9
11
  private refreshAssetMaps;
@@ -13,9 +13,10 @@ exports.SymbolConversion = void 0;
13
13
  const constants_1 = require("../types/constants");
14
14
  const http_client_1 = require("./http-client");
15
15
  class SymbolConversion {
16
- constructor(config) {
16
+ constructor(config, infoClient) {
17
17
  this.assetToIndexMap = new Map();
18
18
  this.initialized = false;
19
+ this.infoClient = infoClient;
19
20
  this.httpClient = new http_client_1.HttpClient({
20
21
  isTestnet: config.isTestnet,
21
22
  timeout: config.timeout,
@@ -46,9 +47,15 @@ class SymbolConversion {
46
47
  refreshAssetMaps() {
47
48
  return __awaiter(this, void 0, void 0, function* () {
48
49
  try {
49
- const perpMeta = yield this.httpClient.info({
50
- type: constants_1.InfoType.META_AND_ASSET_CTXS,
51
- });
50
+ let perpMeta = null;
51
+ if (!this.infoClient) {
52
+ perpMeta = yield this.httpClient.info({
53
+ type: constants_1.InfoType.META_AND_ASSET_CTXS,
54
+ });
55
+ }
56
+ else {
57
+ perpMeta = yield this.infoClient.metaAndAssetCtxs(false);
58
+ }
52
59
  // Verify responses are valid before proceeding
53
60
  if (!perpMeta ||
54
61
  !perpMeta[0] ||
@@ -1,6 +1,6 @@
1
- import type { AllMids, L2Book, Candle, WsFill, WsOrder, WebData2 } 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;
@@ -18,11 +18,11 @@ export declare class WebSocketClient {
18
18
  private readonly url;
19
19
  private readonly config;
20
20
  private subscriptions;
21
- private subscriptionId;
22
21
  private reconnectAttempts;
23
22
  private isConnecting;
24
23
  private pendingSubscriptions;
25
24
  constructor(config: WebSocketClientConfig);
25
+ initMasterAddress(masterAddress: string): void;
26
26
  /**
27
27
  * Connect to the WebSocket
28
28
  */
@@ -43,6 +43,10 @@ export declare class WebSocketClient {
43
43
  * Subscribe to L2 order book for a specific coin
44
44
  */
45
45
  subscribeToL2Book(coin: string, callback: SubscriptionCallback<L2Book>): Subscription;
46
+ /**
47
+ * Subscribe to active asset ctx
48
+ */
49
+ subscribeToActiveAssetCtx(coin: string, callback: SubscriptionCallback<WsActiveAssetCtx>): Subscription;
46
50
  /**
47
51
  * Subscribe to trades for a specific coin
48
52
  */
@@ -54,7 +58,7 @@ export declare class WebSocketClient {
54
58
  /**
55
59
  * Subscribe to user fills
56
60
  */
57
- subscribeToUserFills(callback: SubscriptionCallback<WsFill[]>): Subscription;
61
+ subscribeToUserFills(callback: SubscriptionCallback<WsUserFills>): Subscription;
58
62
  /**
59
63
  * Subscribe to user order updates
60
64
  */
@@ -18,13 +18,21 @@ class WebSocketClient {
18
18
  constructor(config) {
19
19
  this.ws = null;
20
20
  this.subscriptions = new Map();
21
- this.subscriptionId = 0;
22
21
  this.reconnectAttempts = 0;
23
22
  this.isConnecting = false;
24
23
  this.pendingSubscriptions = [];
25
- this.config = Object.assign({ isTestnet: false, autoReconnect: true, reconnectDelay: 3000, maxReconnectAttempts: 5 }, config);
24
+ this.config = {
25
+ isTestnet: false,
26
+ autoReconnect: true,
27
+ reconnectDelay: 3000,
28
+ maxReconnectAttempts: 5,
29
+ masterAddress: config.masterAddress || '',
30
+ };
26
31
  this.url = this.config.isTestnet ? constants_1.WSS_URLS.TESTNET : constants_1.WSS_URLS.PRODUCTION;
27
32
  }
33
+ initMasterAddress(masterAddress) {
34
+ this.config.masterAddress = masterAddress;
35
+ }
28
36
  /**
29
37
  * Connect to the WebSocket
30
38
  */
@@ -57,13 +65,13 @@ class WebSocketClient {
57
65
  this.ws.onopen = () => {
58
66
  this.isConnecting = false;
59
67
  this.reconnectAttempts = 0;
60
- console.log('WebSocket connected');
68
+ console.log('hyperliquid sdk WebSocket connected');
61
69
  // Resubscribe to pending subscriptions
62
- this.pendingSubscriptions.forEach(({ id, message, callback }) => {
70
+ this.pendingSubscriptions.forEach(({ message, callback }) => {
63
71
  var _a;
64
- this.subscriptions.set(id, callback);
65
72
  if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
66
- this.ws.send(JSON.stringify(message));
73
+ this.subscriptions.set(message.subscription.type, callback);
74
+ this.ws.send(JSON.stringify(Object.assign(Object.assign({}, message), { method: 'subscribe' })));
67
75
  }
68
76
  });
69
77
  this.pendingSubscriptions = [];
@@ -72,12 +80,10 @@ class WebSocketClient {
72
80
  this.ws.onmessage = (event) => {
73
81
  try {
74
82
  const data = JSON.parse(event.data);
75
- if (data.method === 'subscription') {
76
- const subscriptionId = data.subscription;
77
- const callback = this.subscriptions.get(subscriptionId);
78
- if (callback) {
79
- callback(data.data);
80
- }
83
+ const type = data.channel;
84
+ const callback = this.subscriptions.get(type);
85
+ if (callback) {
86
+ callback(data.data);
81
87
  }
82
88
  }
83
89
  catch (error) {
@@ -85,9 +91,10 @@ class WebSocketClient {
85
91
  }
86
92
  };
87
93
  this.ws.onclose = () => {
94
+ var _a;
88
95
  this.isConnecting = false;
89
96
  console.log('WebSocket disconnected');
90
- if (this.config.autoReconnect && this.reconnectAttempts < this.config.maxReconnectAttempts) {
97
+ if (this.config.autoReconnect && this.reconnectAttempts < this.config.maxReconnectAttempts && ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
91
98
  this.reconnectAttempts++;
92
99
  console.log(`Reconnecting... (${this.reconnectAttempts}/${this.config.maxReconnectAttempts})`);
93
100
  setTimeout(() => this.connect(), this.config.reconnectDelay);
@@ -117,15 +124,13 @@ class WebSocketClient {
117
124
  */
118
125
  subscribe(message, callback) {
119
126
  var _a;
120
- const id = ++this.subscriptionId;
121
- const subscriptionMessage = Object.assign(Object.assign({}, message), { id });
122
127
  if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
123
- this.subscriptions.set(id, callback);
124
- this.ws.send(JSON.stringify(Object.assign(Object.assign({}, subscriptionMessage), { method: 'subscribe' })));
128
+ this.subscriptions.set(message.subscription.type, callback);
129
+ this.ws.send(JSON.stringify(Object.assign(Object.assign({}, message), { method: 'subscribe' })));
125
130
  }
126
131
  else {
127
132
  // Store pending subscription for when connection is established
128
- this.pendingSubscriptions.push({ id, message: subscriptionMessage, callback });
133
+ this.pendingSubscriptions.push({ message, callback });
129
134
  // Auto-connect if not connected
130
135
  if (!this.isConnecting && (!this.ws || this.ws.readyState === WebSocket.CLOSED)) {
131
136
  this.connect().catch(console.error);
@@ -134,12 +139,12 @@ class WebSocketClient {
134
139
  return {
135
140
  unsubscribe: () => {
136
141
  var _a;
137
- this.subscriptions.delete(id);
142
+ this.subscriptions.delete(message.subscription.type);
138
143
  if (((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN) {
139
- this.ws.send(JSON.stringify(Object.assign(Object.assign({}, subscriptionMessage), { method: 'unsubscribe' })));
144
+ this.ws.send(JSON.stringify(Object.assign(Object.assign({}, message), { method: 'unsubscribe' })));
140
145
  }
141
146
  // Remove from pending subscriptions
142
- this.pendingSubscriptions = this.pendingSubscriptions.filter(sub => sub.id !== id);
147
+ this.pendingSubscriptions = this.pendingSubscriptions.filter(sub => sub.message.subscription.type !== message.subscription.type);
143
148
  },
144
149
  };
145
150
  }
@@ -159,6 +164,14 @@ class WebSocketClient {
159
164
  subscription: { type: 'l2Book', coin },
160
165
  }, callback);
161
166
  }
167
+ /**
168
+ * Subscribe to active asset ctx
169
+ */
170
+ subscribeToActiveAssetCtx(coin, callback) {
171
+ return this.subscribe({
172
+ subscription: { type: 'activeAssetCtx', coin },
173
+ }, callback);
174
+ }
162
175
  /**
163
176
  * Subscribe to trades for a specific coin
164
177
  */
@@ -179,22 +192,31 @@ class WebSocketClient {
179
192
  * Subscribe to user fills
180
193
  */
181
194
  subscribeToUserFills(callback) {
195
+ if (!this.config.masterAddress) {
196
+ throw new Error('masterAddress is empty');
197
+ }
182
198
  return this.subscribe({
183
- subscription: { type: 'userFills', user: this.config.masterAddress },
199
+ subscription: { type: 'userFills', user: this.config.masterAddress, aggregateByTime: true },
184
200
  }, callback);
185
201
  }
186
202
  /**
187
203
  * Subscribe to user order updates
188
204
  */
189
205
  subscribeToUserOrders(callback) {
206
+ if (!this.config.masterAddress) {
207
+ throw new Error('masterAddress is empty');
208
+ }
190
209
  return this.subscribe({
191
- subscription: { type: 'userEvents', user: this.config.masterAddress },
210
+ subscription: { type: 'orderUpdates', user: this.config.masterAddress },
192
211
  }, callback);
193
212
  }
194
213
  /**
195
214
  * Subscribe to user funding updates
196
215
  */
197
216
  subscribeToUserFunding(callback) {
217
+ if (!this.config.masterAddress) {
218
+ throw new Error('masterAddress is empty');
219
+ }
198
220
  return this.subscribe({
199
221
  subscription: { type: 'userFundings', user: this.config.masterAddress },
200
222
  }, callback);
@@ -204,6 +226,9 @@ class WebSocketClient {
204
226
  * Includes positions, margin summary, open orders, and other user data
205
227
  */
206
228
  subscribeToWebData2(callback) {
229
+ if (!this.config.masterAddress) {
230
+ throw new Error('masterAddress is empty');
231
+ }
207
232
  return this.subscribe({
208
233
  subscription: { type: 'webData2', user: this.config.masterAddress },
209
234
  }, 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
  */
@@ -46,8 +46,4 @@ export declare class HyperliquidSDK {
46
46
  * Quick helper: Get account summary with essential info
47
47
  */
48
48
  getAccountSummary(): Promise<ClearinghouseState>;
49
- /**
50
- * Quick helper: Get market overview
51
- */
52
- getMarketOverview(): Promise<any>;
53
49
  }
@@ -21,20 +21,19 @@ const symbolConversion_1 = require("./client/symbolConversion");
21
21
  */
22
22
  class HyperliquidSDK {
23
23
  constructor(config) {
24
- // Initialize info client (always available)
25
- this.symbolConversion = new symbolConversion_1.SymbolConversion({
26
- isTestnet: config.isTestnet,
27
- timeout: config.timeout,
28
- });
29
24
  this.info = new info_client_1.InfoClient({
30
25
  masterAddress: config.masterAddress,
31
26
  isTestnet: config.isTestnet,
32
27
  timeout: config.timeout,
33
28
  });
29
+ this.symbolConversion = new symbolConversion_1.SymbolConversion({
30
+ isTestnet: config.isTestnet,
31
+ timeout: config.timeout,
32
+ }, this.info);
34
33
  this.masterAddress = config.masterAddress;
35
34
  this.configParams = config;
36
35
  // Initialize exchange client only if private key is provided
37
- if (config.agentPrivateKey) {
36
+ if (config.masterAddress) {
38
37
  this.exchange = new exchange_client_1.ExchangeClient({
39
38
  masterAddress: config.masterAddress,
40
39
  agentPrivateKey: config.agentPrivateKey,
@@ -52,12 +51,25 @@ class HyperliquidSDK {
52
51
  * Get wallet address (only available if private key was provided)
53
52
  */
54
53
  get address() {
55
- return this.masterAddress;
54
+ return this.masterAddress || '';
56
55
  }
57
- updateExchangeAgent(agentPrivateKey, agentPublicKey, agentName) {
58
- this.exchange = new exchange_client_1.ExchangeClient(Object.assign(Object.assign({}, this.configParams), { symbolConversion: this.symbolConversion, agentPrivateKey,
59
- agentPublicKey,
60
- 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
+ }
61
73
  }
62
74
  /**
63
75
  * Connect to WebSocket for real-time data
@@ -87,18 +99,5 @@ class HyperliquidSDK {
87
99
  return this.info.getClearingHouseState();
88
100
  });
89
101
  }
90
- /**
91
- * Quick helper: Get market overview
92
- */
93
- getMarketOverview() {
94
- return __awaiter(this, void 0, void 0, function* () {
95
- const [meta, assetCtxs] = yield this.info.metaAndAssetCtxs();
96
- return {
97
- assets: meta.universe,
98
- assetContexts: assetCtxs,
99
- marginTables: meta.marginTables,
100
- };
101
- });
102
- }
103
102
  }
104
103
  exports.HyperliquidSDK = HyperliquidSDK;
@@ -27,6 +27,8 @@ export declare enum ExchangeType {
27
27
  }
28
28
  export declare enum InfoType {
29
29
  ALL_MIDS = "allMids",
30
+ USER_NON_FUNDING_LEDGER_UPDATES = "userNonFundingLedgerUpdates",
31
+ USER_HISTORICAL_ORDERS = "historicalOrders",
30
32
  USER_OPEN_ORDERS = "openOrders",
31
33
  USER_FILLS = "userFills",
32
34
  USER_FILLS_BY_TIME = "userFillsByTime",
@@ -35,14 +37,13 @@ export declare enum InfoType {
35
37
  CANDLES_SNAPSHOT = "candleSnapshot",
36
38
  META_AND_ASSET_CTXS = "metaAndAssetCtxs",
37
39
  USER_FUNDING = "userFunding",
38
- USER_NON_FUNDING_LEDGER_UPDATES = "userNonFundingLedgerUpdates",
39
40
  FUNDING_HISTORY = "fundingHistory",
40
41
  CLEARINGHOUSE_STATE = "clearinghouseState",
41
42
  META = "meta",
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"
@@ -35,6 +35,8 @@ var ExchangeType;
35
35
  var InfoType;
36
36
  (function (InfoType) {
37
37
  InfoType["ALL_MIDS"] = "allMids";
38
+ InfoType["USER_NON_FUNDING_LEDGER_UPDATES"] = "userNonFundingLedgerUpdates";
39
+ InfoType["USER_HISTORICAL_ORDERS"] = "historicalOrders";
38
40
  InfoType["USER_OPEN_ORDERS"] = "openOrders";
39
41
  InfoType["USER_FILLS"] = "userFills";
40
42
  InfoType["USER_FILLS_BY_TIME"] = "userFillsByTime";
@@ -43,14 +45,13 @@ var InfoType;
43
45
  InfoType["CANDLES_SNAPSHOT"] = "candleSnapshot";
44
46
  InfoType["META_AND_ASSET_CTXS"] = "metaAndAssetCtxs";
45
47
  InfoType["USER_FUNDING"] = "userFunding";
46
- InfoType["USER_NON_FUNDING_LEDGER_UPDATES"] = "userNonFundingLedgerUpdates";
47
48
  InfoType["FUNDING_HISTORY"] = "fundingHistory";
48
49
  InfoType["CLEARINGHOUSE_STATE"] = "clearinghouseState";
49
50
  InfoType["META"] = "meta";
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";
@@ -4,6 +4,33 @@ export type TriggerType = 'tp' | 'sl';
4
4
  export type Side = 'A' | 'B';
5
5
  export type AssetId = number;
6
6
  export type OrderId = number;
7
+ export interface MarketOverview {
8
+ assets: AssetInfo[];
9
+ assetContexts: AssetCtx[];
10
+ marginTables: [number, MarginTable][];
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
+ }
7
34
  export interface OpenOrder {
8
35
  coin: string;
9
36
  side: Side;
@@ -52,6 +79,7 @@ export type OrderType = LimitOrderType | MarketOrderType | StopOrderType;
52
79
  export interface AssetInfo {
53
80
  name: string;
54
81
  szDecimals: number;
82
+ marginTableId: number;
55
83
  maxLeverage: number;
56
84
  onlyIsolated?: boolean;
57
85
  isDelisted?: boolean;
@@ -121,6 +149,24 @@ export interface UserFill {
121
149
  export type UserFills = {
122
150
  [asset: string]: UserFill[];
123
151
  };
152
+ export type UserNonFundingLedgerUpdates = {
153
+ time: number;
154
+ hash: string;
155
+ delta: {
156
+ type: string;
157
+ usdc?: string;
158
+ vault?: string;
159
+ user?: string;
160
+ token?: string;
161
+ amount?: string;
162
+ requestedUsd?: string;
163
+ commission?: string;
164
+ closingCost?: string;
165
+ basis?: string;
166
+ netWithdrawnUsd?: string;
167
+ toPerp?: boolean;
168
+ };
169
+ };
124
170
  export interface OrderResponse {
125
171
  status: 'ok';
126
172
  response: {
@@ -133,6 +179,7 @@ export interface OrderResponse {
133
179
  filled?: {
134
180
  totalSz: string;
135
181
  avgPx: string;
182
+ oid: number;
136
183
  };
137
184
  error?: string;
138
185
  }>;
@@ -185,6 +232,11 @@ export interface WsOrder {
185
232
  children?: WsOrder[];
186
233
  user: string;
187
234
  }
235
+ export interface WsUserFills {
236
+ isSnapshot?: boolean;
237
+ user: string;
238
+ fills: WsFill[];
239
+ }
188
240
  export interface WsFill {
189
241
  coin: string;
190
242
  px: string;
@@ -199,8 +251,14 @@ export interface WsFill {
199
251
  crossed: boolean;
200
252
  fee: string;
201
253
  tid: number;
254
+ liquidation?: {
255
+ liquidatedUser: string;
256
+ markPx: string;
257
+ method: string;
258
+ };
202
259
  }
203
260
  export interface AssetCtx {
261
+ dayBaseVlm: string;
204
262
  dayNtlVlm: string;
205
263
  funding: string;
206
264
  impactPxs: [string, string];
@@ -219,6 +277,18 @@ export interface L2Book {
219
277
  levels: [WsLevel[], WsLevel[]];
220
278
  time: number;
221
279
  }
280
+ export interface WsActiveAssetCtx {
281
+ coin: string;
282
+ ctx: {
283
+ dayNtlVlm: string;
284
+ prevDayPx: string;
285
+ markPx: string;
286
+ midPx?: string;
287
+ funding: string;
288
+ openInterest: string;
289
+ oraclePx: string;
290
+ };
291
+ }
222
292
  export interface Candle {
223
293
  t: number;
224
294
  T: number;
@@ -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.2",
3
+ "version": "1.0.0-beta.20",
4
4
  "description": "Simplified Hyperliquid Perpetuals Trading SDK for Frontend Applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",