@lifi/perps-sdk-provider-hyperliquid 20.0.0 → 21.0.0
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 +45 -0
- package/dist/cjs/HyperliquidProvider.d.ts.map +1 -1
- package/dist/cjs/HyperliquidProvider.js +2 -2
- package/dist/cjs/HyperliquidProvider.js.map +1 -1
- package/dist/cjs/services/getAccount.d.ts.map +1 -1
- package/dist/cjs/services/getAccount.js +5 -7
- package/dist/cjs/services/getAccount.js.map +1 -1
- package/dist/cjs/services/getActivity.d.ts.map +1 -1
- package/dist/cjs/services/getActivity.js +68 -44
- package/dist/cjs/services/getActivity.js.map +1 -1
- package/dist/cjs/services/getFills.d.ts.map +1 -1
- package/dist/cjs/services/getFills.js +30 -38
- package/dist/cjs/services/getFills.js.map +1 -1
- package/dist/cjs/services/getWithdrawableBalances.js +1 -1
- package/dist/cjs/services/getWithdrawableBalances.js.map +1 -1
- package/dist/cjs/services/historyPagination.d.ts +22 -0
- package/dist/cjs/services/historyPagination.d.ts.map +1 -0
- package/dist/cjs/services/historyPagination.js +125 -0
- package/dist/cjs/services/historyPagination.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +1 -1
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +3 -3
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/liquidation.js +1 -1
- package/dist/cjs/utils/liquidation.js.map +1 -1
- package/dist/cjs/utils/orderFormatting.d.ts +3 -2
- package/dist/cjs/utils/orderFormatting.d.ts.map +1 -1
- package/dist/cjs/utils/orderFormatting.js +4 -4
- package/dist/cjs/utils/orderFormatting.js.map +1 -1
- package/dist/cjs/utils/spotBalance.d.ts.map +1 -1
- package/dist/cjs/utils/spotBalance.js +2 -2
- package/dist/cjs/utils/spotBalance.js.map +1 -1
- package/dist/cjs/utils/withdrawableBalances.d.ts +1 -1
- package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -1
- package/dist/cjs/utils/withdrawableBalances.js +7 -4
- package/dist/cjs/utils/withdrawableBalances.js.map +1 -1
- package/dist/cjs/websocket/HyperliquidWsProvider.d.ts +6 -0
- package/dist/cjs/websocket/HyperliquidWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/HyperliquidWsProvider.js +135 -37
- package/dist/cjs/websocket/HyperliquidWsProvider.js.map +1 -1
- package/dist/esm/HyperliquidProvider.d.ts.map +1 -1
- package/dist/esm/HyperliquidProvider.js +3 -3
- package/dist/esm/HyperliquidProvider.js.map +1 -1
- package/dist/esm/services/getAccount.d.ts.map +1 -1
- package/dist/esm/services/getAccount.js +6 -5
- package/dist/esm/services/getAccount.js.map +1 -1
- package/dist/esm/services/getActivity.d.ts +3 -13
- package/dist/esm/services/getActivity.d.ts.map +1 -1
- package/dist/esm/services/getActivity.js +74 -64
- package/dist/esm/services/getActivity.js.map +1 -1
- package/dist/esm/services/getFills.d.ts +2 -14
- package/dist/esm/services/getFills.d.ts.map +1 -1
- package/dist/esm/services/getFills.js +32 -58
- package/dist/esm/services/getFills.js.map +1 -1
- package/dist/esm/services/getWithdrawableBalances.js +1 -1
- package/dist/esm/services/getWithdrawableBalances.js.map +1 -1
- package/dist/esm/services/historyPagination.d.ts +22 -0
- package/dist/esm/services/historyPagination.d.ts.map +1 -0
- package/dist/esm/services/historyPagination.js +121 -0
- package/dist/esm/services/historyPagination.js.map +1 -0
- package/dist/esm/utils/index.d.ts +1 -1
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/liquidation.js +2 -2
- package/dist/esm/utils/liquidation.js.map +1 -1
- package/dist/esm/utils/orderFormatting.d.ts +14 -12
- package/dist/esm/utils/orderFormatting.d.ts.map +1 -1
- package/dist/esm/utils/orderFormatting.js +14 -14
- package/dist/esm/utils/orderFormatting.js.map +1 -1
- package/dist/esm/utils/spotBalance.d.ts.map +1 -1
- package/dist/esm/utils/spotBalance.js +3 -3
- package/dist/esm/utils/spotBalance.js.map +1 -1
- package/dist/esm/utils/withdrawableBalances.d.ts +1 -1
- package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -1
- package/dist/esm/utils/withdrawableBalances.js +7 -4
- package/dist/esm/utils/withdrawableBalances.js.map +1 -1
- package/dist/esm/websocket/HyperliquidWsProvider.d.ts +6 -0
- package/dist/esm/websocket/HyperliquidWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/HyperliquidWsProvider.js +138 -50
- package/dist/esm/websocket/HyperliquidWsProvider.js.map +1 -1
- package/dist/types/HyperliquidProvider.d.ts.map +1 -1
- package/dist/types/services/getAccount.d.ts.map +1 -1
- package/dist/types/services/getActivity.d.ts +3 -13
- package/dist/types/services/getActivity.d.ts.map +1 -1
- package/dist/types/services/getFills.d.ts +2 -14
- package/dist/types/services/getFills.d.ts.map +1 -1
- package/dist/types/services/historyPagination.d.ts +22 -0
- package/dist/types/services/historyPagination.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +1 -1
- package/dist/types/utils/index.d.ts.map +1 -1
- package/dist/types/utils/orderFormatting.d.ts +14 -12
- package/dist/types/utils/orderFormatting.d.ts.map +1 -1
- package/dist/types/utils/spotBalance.d.ts.map +1 -1
- package/dist/types/utils/withdrawableBalances.d.ts +1 -1
- package/dist/types/utils/withdrawableBalances.d.ts.map +1 -1
- package/dist/types/websocket/HyperliquidWsProvider.d.ts +6 -0
- package/dist/types/websocket/HyperliquidWsProvider.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/HyperliquidProvider.ts +6 -5
- package/src/services/getAccount.ts +18 -18
- package/src/services/getActivity.ts +133 -104
- package/src/services/getFills.ts +42 -95
- package/src/services/getWithdrawableBalances.ts +1 -1
- package/src/services/historyPagination.ts +169 -0
- package/src/utils/index.ts +2 -2
- package/src/utils/liquidation.ts +2 -2
- package/src/utils/orderFormatting.ts +20 -16
- package/src/utils/spotBalance.ts +5 -3
- package/src/utils/withdrawableBalances.ts +12 -7
- package/src/websocket/HyperliquidWsProvider.ts +151 -63
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
calculateTransferable,
|
|
2
3
|
DecodeChain,
|
|
3
4
|
getMarketRegistry,
|
|
4
5
|
isActiveMarket,
|
|
@@ -137,20 +138,21 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
137
138
|
private readonly clearinghouseRefs = new Map<string, number>()
|
|
138
139
|
private readonly client: PerpsSDKClient | undefined
|
|
139
140
|
private readonly registry: MarketRegistry | undefined
|
|
140
|
-
//
|
|
141
|
-
// honest solely for modes whose collateral lives per-dex. The abstraction
|
|
142
|
-
// mode is read per subscribed user (null = never set = standard) and
|
|
143
|
-
// selects the summary source: standard/dexAbstraction emit the equity
|
|
144
|
-
// summary straight from clearinghouse frames, unified/portfolio run the
|
|
145
|
-
// spot-fed pipeline below. Frames arriving before the first read are held
|
|
146
|
-
// and released on resolution (delayed, never wrong). The read refreshes on
|
|
147
|
-
// fresh summary subscribes and once older than the TTL, and drops with
|
|
148
|
-
// the user's last clearinghouse subscription.
|
|
141
|
+
// Spot-funded modes need both spot valuation and clearinghouse margin state.
|
|
149
142
|
private readonly abstractionByUser = new Map<
|
|
150
143
|
string,
|
|
151
144
|
{ mode: HlAbstractionMode | null; readAt: number } | 'pending'
|
|
152
145
|
>()
|
|
146
|
+
private readonly summaryUsers = new Set<string>()
|
|
147
|
+
private readonly abstractionReadsByUser = new Map<string, symbol>()
|
|
153
148
|
private readonly spotRefs = new Map<string, number>()
|
|
149
|
+
private spotWireEpoch = 0
|
|
150
|
+
private readonly latestSpotByUser = new Map<string, HlWsSpotStateData>()
|
|
151
|
+
private readonly revalueSpotBalances = () => {
|
|
152
|
+
for (const user of this.latestSpotByUser.keys()) {
|
|
153
|
+
this.emitSpotBalances(user)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
154
156
|
// Unified/portfolio summary pipeline: collateral lives in spot, margin and
|
|
155
157
|
// uPnL on the positions — the summary recomputes from the latest of both
|
|
156
158
|
// envelopes with the same gross calculator the REST getAccountSummary uses.
|
|
@@ -310,16 +312,15 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
310
312
|
// Markets context aggregates slower asset-context feeds with fast mid/mark
|
|
311
313
|
// ticks from `fastAssetCtxs`.
|
|
312
314
|
if (sub.channel === 'marketsContext') {
|
|
315
|
+
const epoch = this.spotWireEpoch
|
|
313
316
|
const entries = this.getMarketsContextSubEntries()
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
return () => {
|
|
322
|
-
for (const { subKey, payload } of entries) {
|
|
317
|
+
let acquired = 0
|
|
318
|
+
const release = () => {
|
|
319
|
+
if (epoch !== this.spotWireEpoch) {
|
|
320
|
+
return
|
|
321
|
+
}
|
|
322
|
+
for (let index = 0; index < acquired; index++) {
|
|
323
|
+
const { subKey, payload } = entries[index]
|
|
323
324
|
this.unregisterSub(subKey)
|
|
324
325
|
this.rws.send(
|
|
325
326
|
JSON.stringify({ method: 'unsubscribe', subscription: payload })
|
|
@@ -329,15 +330,25 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
329
330
|
this.spotCtxByMarketId = {}
|
|
330
331
|
this.fastCtxByMarketId = {}
|
|
331
332
|
this.marketsContextByMarketId = {}
|
|
333
|
+
acquired = 0
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
for (const { subKey, payload } of entries) {
|
|
337
|
+
await this.registerSub(subKey, payload)
|
|
338
|
+
acquired++
|
|
339
|
+
}
|
|
340
|
+
await this.rws.ready()
|
|
341
|
+
return release
|
|
342
|
+
} catch (error) {
|
|
343
|
+
release()
|
|
344
|
+
throw error
|
|
332
345
|
}
|
|
333
346
|
}
|
|
334
347
|
|
|
335
348
|
// The spot wire is shared between the public spotBalances channel and
|
|
336
349
|
// the unified-summary pipeline; refcounted like the clearinghouse sub.
|
|
337
350
|
if (sub.channel === 'spotBalances') {
|
|
338
|
-
|
|
339
|
-
await this.rws.ready()
|
|
340
|
-
return release
|
|
351
|
+
return this.acquireSpotWire(sub.address)
|
|
341
352
|
}
|
|
342
353
|
|
|
343
354
|
// `positions` and `accountSummary` are two views over the same wire
|
|
@@ -348,20 +359,25 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
348
359
|
const payload = this.toHlPayload(sub)
|
|
349
360
|
const count = this.clearinghouseRefs.get(user) ?? 0
|
|
350
361
|
this.clearinghouseRefs.set(user, count + 1)
|
|
362
|
+
if (sub.channel === 'accountSummary') {
|
|
363
|
+
this.summaryUsers.add(user)
|
|
364
|
+
}
|
|
351
365
|
if (count === 0) {
|
|
352
366
|
await this.registerSub(wireKey, payload)
|
|
353
367
|
}
|
|
354
368
|
await this.rws.ready()
|
|
355
369
|
return () => {
|
|
370
|
+
if (sub.channel === 'accountSummary') {
|
|
371
|
+
this.summaryUsers.delete(user)
|
|
372
|
+
this.abstractionReadsByUser.delete(user)
|
|
373
|
+
this.abstractionByUser.delete(user)
|
|
374
|
+
this.heldSummaryByUser.delete(user)
|
|
375
|
+
this.syncUnifiedPipeline(user, user, null)
|
|
376
|
+
}
|
|
356
377
|
const remaining = (this.clearinghouseRefs.get(user) ?? 1) - 1
|
|
357
378
|
if (remaining <= 0) {
|
|
358
379
|
this.clearinghouseRefs.delete(user)
|
|
359
|
-
// Drop the summary gate's mode read (and its pipeline) with the
|
|
360
|
-
// subscription so a resubscribe reflects an account-mode change.
|
|
361
|
-
this.abstractionByUser.delete(user)
|
|
362
|
-
this.heldSummaryByUser.delete(user)
|
|
363
380
|
this.latestPerpsByUser.delete(user)
|
|
364
|
-
this.syncUnifiedPipeline(user, user, null)
|
|
365
381
|
this.unregisterSub(wireKey)
|
|
366
382
|
this.rws.send(
|
|
367
383
|
JSON.stringify({ method: 'unsubscribe', subscription: payload })
|
|
@@ -489,9 +505,19 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
489
505
|
}
|
|
490
506
|
|
|
491
507
|
protected override onClose(): void {
|
|
508
|
+
this.spotWireEpoch++
|
|
492
509
|
this.perpCtxBySubDex.clear()
|
|
493
510
|
this.spotCtxByMarketId = {}
|
|
494
511
|
this.fastCtxByMarketId = {}
|
|
512
|
+
this.latestSpotByUser.clear()
|
|
513
|
+
this.spotRefs.clear()
|
|
514
|
+
this.clearinghouseRefs.clear()
|
|
515
|
+
this.summaryUsers.clear()
|
|
516
|
+
this.abstractionReadsByUser.clear()
|
|
517
|
+
this.abstractionByUser.clear()
|
|
518
|
+
this.heldSummaryByUser.clear()
|
|
519
|
+
this.latestPerpsByUser.clear()
|
|
520
|
+
this.unifiedSummaryByUser.clear()
|
|
495
521
|
this.marketsContextByMarketId = {}
|
|
496
522
|
this.orderbookKeysByMarketId.clear()
|
|
497
523
|
this.latestOrderbookByMarketId.clear()
|
|
@@ -1206,22 +1232,23 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1206
1232
|
// Lowercased in the payload too, so releases from either holder (public
|
|
1207
1233
|
// channel or pipeline) unsubscribe with an identical payload.
|
|
1208
1234
|
const user = address.toLowerCase()
|
|
1235
|
+
const epoch = this.spotWireEpoch
|
|
1209
1236
|
const wireKey = `spot:${user}`
|
|
1210
1237
|
const payload = { type: 'spotState', user }
|
|
1211
1238
|
const count = this.spotRefs.get(user) ?? 0
|
|
1212
1239
|
this.spotRefs.set(user, count + 1)
|
|
1213
|
-
|
|
1214
|
-
await this.registerSub(wireKey, payload)
|
|
1215
|
-
}
|
|
1240
|
+
let releasePrices: (() => void) | undefined
|
|
1216
1241
|
let released = false
|
|
1217
|
-
|
|
1218
|
-
if (released) {
|
|
1242
|
+
const release = () => {
|
|
1243
|
+
if (released || epoch !== this.spotWireEpoch) {
|
|
1219
1244
|
return
|
|
1220
1245
|
}
|
|
1221
1246
|
released = true
|
|
1247
|
+
releasePrices?.()
|
|
1222
1248
|
const remaining = (this.spotRefs.get(user) ?? 1) - 1
|
|
1223
1249
|
if (remaining <= 0) {
|
|
1224
1250
|
this.spotRefs.delete(user)
|
|
1251
|
+
this.latestSpotByUser.delete(user)
|
|
1225
1252
|
this.unregisterSub(wireKey)
|
|
1226
1253
|
this.rws.send(
|
|
1227
1254
|
JSON.stringify({ method: 'unsubscribe', subscription: payload })
|
|
@@ -1230,6 +1257,21 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1230
1257
|
this.spotRefs.set(user, remaining)
|
|
1231
1258
|
}
|
|
1232
1259
|
}
|
|
1260
|
+
try {
|
|
1261
|
+
if (count === 0) {
|
|
1262
|
+
await this.registerSub(wireKey, payload)
|
|
1263
|
+
}
|
|
1264
|
+
if (epoch === this.spotWireEpoch) {
|
|
1265
|
+
releasePrices = await super.subscribe(
|
|
1266
|
+
{ channel: 'marketsContext', dex: this.providerKey },
|
|
1267
|
+
this.revalueSpotBalances
|
|
1268
|
+
)
|
|
1269
|
+
}
|
|
1270
|
+
return release
|
|
1271
|
+
} catch (error) {
|
|
1272
|
+
release()
|
|
1273
|
+
throw error
|
|
1274
|
+
}
|
|
1233
1275
|
}
|
|
1234
1276
|
|
|
1235
1277
|
/** Start or stop the spot-fed unified pipeline to match the resolved mode. */
|
|
@@ -1241,12 +1283,22 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1241
1283
|
const unified = !HyperliquidWsProvider.summaryComputableFor(mode)
|
|
1242
1284
|
const existing = this.unifiedSummaryByUser.get(key)
|
|
1243
1285
|
if (unified && existing === undefined) {
|
|
1244
|
-
this.
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
}
|
|
1286
|
+
const epoch = this.spotWireEpoch
|
|
1287
|
+
const releaseSpot = this.acquireSpotWire(address).catch((error) => {
|
|
1288
|
+
if (this.unifiedSummaryByUser.get(key)?.releaseSpot === releaseSpot) {
|
|
1289
|
+
this.unifiedSummaryByUser.delete(key)
|
|
1290
|
+
}
|
|
1291
|
+
wsLog.handlerFailure(this.providerKey, error)
|
|
1292
|
+
return () => {}
|
|
1249
1293
|
})
|
|
1294
|
+
if (epoch !== this.spotWireEpoch) {
|
|
1295
|
+
return
|
|
1296
|
+
}
|
|
1297
|
+
this.unifiedSummaryByUser.set(key, { releaseSpot })
|
|
1298
|
+
const spot = this.latestSpotByUser.get(key)
|
|
1299
|
+
if (spot !== undefined) {
|
|
1300
|
+
this.handleSpotState(spot)
|
|
1301
|
+
}
|
|
1250
1302
|
} else if (!unified && existing !== undefined) {
|
|
1251
1303
|
this.unifiedSummaryByUser.delete(key)
|
|
1252
1304
|
void existing.releaseSpot.then((release) => release())
|
|
@@ -1303,6 +1355,9 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1303
1355
|
}
|
|
1304
1356
|
) {
|
|
1305
1357
|
const key = user.toLowerCase()
|
|
1358
|
+
if (!this.summaryUsers.has(key)) {
|
|
1359
|
+
return
|
|
1360
|
+
}
|
|
1306
1361
|
// Without a client there is no way to read the mode; keep the historic
|
|
1307
1362
|
// always-emit behavior rather than silencing every standalone consumer.
|
|
1308
1363
|
if (this.client === undefined) {
|
|
@@ -1340,7 +1395,7 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1340
1395
|
refreshing?: { mode: HlAbstractionMode | null; readAt: number }
|
|
1341
1396
|
) {
|
|
1342
1397
|
const client = this.client
|
|
1343
|
-
if (client === undefined) {
|
|
1398
|
+
if (client === undefined || !this.summaryUsers.has(key)) {
|
|
1344
1399
|
return
|
|
1345
1400
|
}
|
|
1346
1401
|
// A background refresh keeps the current entry live (stamped now so
|
|
@@ -1349,6 +1404,8 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1349
1404
|
key,
|
|
1350
1405
|
refreshing ? { ...refreshing, readAt: Date.now() } : 'pending'
|
|
1351
1406
|
)
|
|
1407
|
+
const read = Symbol()
|
|
1408
|
+
this.abstractionReadsByUser.set(key, read)
|
|
1352
1409
|
// "Never set abstraction" is a successful 200 `null` body — only a fetch
|
|
1353
1410
|
// failure clears a first read so the next frame retries it. A failed
|
|
1354
1411
|
// refresh keeps the previous mode until the TTL passes again.
|
|
@@ -1358,6 +1415,10 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1358
1415
|
hlInfoOptions(client)
|
|
1359
1416
|
).then(
|
|
1360
1417
|
(mode) => {
|
|
1418
|
+
if (this.abstractionReadsByUser.get(key) !== read) {
|
|
1419
|
+
return
|
|
1420
|
+
}
|
|
1421
|
+
this.abstractionReadsByUser.delete(key)
|
|
1361
1422
|
this.abstractionByUser.set(key, { mode, readAt: Date.now() })
|
|
1362
1423
|
this.syncUnifiedPipeline(key, user, mode)
|
|
1363
1424
|
const held = this.heldSummaryByUser.get(key)
|
|
@@ -1370,6 +1431,10 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1370
1431
|
}
|
|
1371
1432
|
},
|
|
1372
1433
|
(error) => {
|
|
1434
|
+
if (this.abstractionReadsByUser.get(key) !== read) {
|
|
1435
|
+
return
|
|
1436
|
+
}
|
|
1437
|
+
this.abstractionReadsByUser.delete(key)
|
|
1373
1438
|
if (!refreshing) {
|
|
1374
1439
|
this.abstractionByUser.delete(key)
|
|
1375
1440
|
}
|
|
@@ -1401,35 +1466,10 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1401
1466
|
|
|
1402
1467
|
private handleSpotState(data: HlWsSpotStateData) {
|
|
1403
1468
|
const user = data.user.toLowerCase()
|
|
1469
|
+
this.latestSpotByUser.set(user, data)
|
|
1404
1470
|
const markets = this.registry?.activeMarkets ?? []
|
|
1405
|
-
const priceById = spotPriceById(markets, this.mergedMids())
|
|
1406
|
-
const rows = data.spotState.balances
|
|
1407
|
-
.filter(
|
|
1408
|
-
(balance) =>
|
|
1409
|
-
!assetIsOutcome(balance.coin) &&
|
|
1410
|
-
toWireBig(balance.total, 'spotState.balances.total').gt(0)
|
|
1411
|
-
)
|
|
1412
|
-
.map((balance) => ({
|
|
1413
|
-
balance: spotBalance(
|
|
1414
|
-
spotAssetFromToken(balance),
|
|
1415
|
-
balance.total,
|
|
1416
|
-
priceById
|
|
1417
|
-
),
|
|
1418
|
-
hold: balance.hold,
|
|
1419
|
-
}))
|
|
1420
|
-
this.emit(`spotState:${user}`, {
|
|
1421
|
-
channel: 'spotBalances',
|
|
1422
|
-
data: rows.map(({ balance, hold }) => ({ ...balance, locked: hold })),
|
|
1423
|
-
})
|
|
1424
|
-
|
|
1425
1471
|
const pipeline = this.unifiedSummaryByUser.get(user)
|
|
1426
1472
|
if (pipeline !== undefined) {
|
|
1427
|
-
const quoteAssetIds = new Set(markets.map((m) => m.quoteAsset.id))
|
|
1428
|
-
// Same partition as getAccount so REST and WS agree on collateral.
|
|
1429
|
-
pipeline.spot = partitionSpotBalances(
|
|
1430
|
-
rows.map(({ balance }) => balance),
|
|
1431
|
-
quoteAssetIds
|
|
1432
|
-
)
|
|
1433
1473
|
const settlementAssetId = markets.find(
|
|
1434
1474
|
(m) => m.categoryId === this.providerKey
|
|
1435
1475
|
)?.quoteAsset.id
|
|
@@ -1442,6 +1482,54 @@ export class HyperliquidWsProvider extends WsProviderBase<object> {
|
|
|
1442
1482
|
} else {
|
|
1443
1483
|
pipeline.availableMargin = available
|
|
1444
1484
|
}
|
|
1485
|
+
}
|
|
1486
|
+
this.emitSpotBalances(user)
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
private emitSpotBalances(user: string) {
|
|
1490
|
+
const data = this.latestSpotByUser.get(user)
|
|
1491
|
+
if (data === undefined) {
|
|
1492
|
+
return
|
|
1493
|
+
}
|
|
1494
|
+
const markets = this.registry?.activeMarkets ?? []
|
|
1495
|
+
const priceById = spotPriceById(markets, this.mergedMids())
|
|
1496
|
+
const balances = data.spotState.balances.filter(
|
|
1497
|
+
(balance) =>
|
|
1498
|
+
!assetIsOutcome(balance.coin) &&
|
|
1499
|
+
toWireBig(balance.total, 'spotState.balances.total').gt(0)
|
|
1500
|
+
)
|
|
1501
|
+
const pipeline = this.unifiedSummaryByUser.get(user)
|
|
1502
|
+
// Known spot markets await a price; unlisted tokens keep their unpriced balance.
|
|
1503
|
+
if (
|
|
1504
|
+
balances.some((balance) => priceById.get(String(balance.token)) === 0)
|
|
1505
|
+
) {
|
|
1506
|
+
if (pipeline !== undefined) {
|
|
1507
|
+
pipeline.spot = undefined
|
|
1508
|
+
}
|
|
1509
|
+
return
|
|
1510
|
+
}
|
|
1511
|
+
const rows = balances.map((balance) => {
|
|
1512
|
+
const total = toWireBig(balance.total, 'spotState.balances.total')
|
|
1513
|
+
return {
|
|
1514
|
+
...spotBalance(spotAssetFromToken(balance), balance.total, priceById),
|
|
1515
|
+
locked: balance.hold,
|
|
1516
|
+
transferable: calculateTransferable(
|
|
1517
|
+
total
|
|
1518
|
+
.minus(toWireBig(balance.hold, 'spotState.balances.hold'))
|
|
1519
|
+
.toFixed(),
|
|
1520
|
+
total.toFixed()
|
|
1521
|
+
),
|
|
1522
|
+
}
|
|
1523
|
+
})
|
|
1524
|
+
this.emit(`spotState:${user}`, {
|
|
1525
|
+
channel: 'spotBalances',
|
|
1526
|
+
data: rows,
|
|
1527
|
+
})
|
|
1528
|
+
if (pipeline !== undefined) {
|
|
1529
|
+
pipeline.spot = partitionSpotBalances(
|
|
1530
|
+
rows,
|
|
1531
|
+
new Set(markets.map((market) => market.quoteAsset.id))
|
|
1532
|
+
)
|
|
1445
1533
|
this.emitUnifiedSummary(user)
|
|
1446
1534
|
}
|
|
1447
1535
|
}
|