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