@lifi/perps-sdk-provider-ondo 22.0.0 → 22.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.
- package/README.md +14 -0
- package/dist/cjs/websocket/OndoWsProvider.d.ts +6 -0
- package/dist/cjs/websocket/OndoWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/OndoWsProvider.js +125 -33
- package/dist/cjs/websocket/OndoWsProvider.js.map +1 -1
- package/dist/esm/websocket/OndoWsProvider.d.ts +7 -5
- package/dist/esm/websocket/OndoWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/OndoWsProvider.js +127 -43
- package/dist/esm/websocket/OndoWsProvider.js.map +1 -1
- package/dist/types/websocket/OndoWsProvider.d.ts +7 -5
- package/dist/types/websocket/OndoWsProvider.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/websocket/OndoWsProvider.ts +156 -45
|
@@ -92,6 +92,7 @@ interface SubState {
|
|
|
92
92
|
frame: { channel: string; markets?: string[]; resolution?: string }
|
|
93
93
|
needsAuth: boolean
|
|
94
94
|
address?: Address
|
|
95
|
+
authGeneration?: number
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
/**
|
|
@@ -138,6 +139,7 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
138
139
|
* positions frame, which is what moves them.
|
|
139
140
|
*/
|
|
140
141
|
private accountSummary: OndoAccountBalance | undefined
|
|
142
|
+
private accountSummaryGeneration = 0
|
|
141
143
|
|
|
142
144
|
/**
|
|
143
145
|
* Serializes the balance re-read: a single trade event emits a fills frame
|
|
@@ -159,6 +161,9 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
159
161
|
/** The single address this connection's login is bound to. */
|
|
160
162
|
private accountAddress: string | undefined
|
|
161
163
|
private loginPromise: Promise<void> | undefined
|
|
164
|
+
private authGeneration = 0
|
|
165
|
+
private authBindingRefs = 0
|
|
166
|
+
private closeGeneration = 0
|
|
162
167
|
|
|
163
168
|
constructor(
|
|
164
169
|
wsUrl: string = DEFAULT_ONDO_WS_URL,
|
|
@@ -240,18 +245,28 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
240
245
|
const wireSubs = this.resolveChannel(sub)
|
|
241
246
|
const needsLogin =
|
|
242
247
|
isAuthChannel(sub.channel) || sub.channel === 'accountSummary'
|
|
248
|
+
const closeGeneration = this.closeGeneration
|
|
249
|
+
let authGeneration: number | undefined
|
|
243
250
|
|
|
244
|
-
let boundHere = false
|
|
245
251
|
if (needsLogin) {
|
|
246
252
|
const { address } = sub as { address: Address }
|
|
247
253
|
// Reject before the address binds and before any wire sub registers: a
|
|
248
254
|
// registered sub whose login can never succeed is retried on every
|
|
249
255
|
// reopen without the caller ever seeing the failure.
|
|
250
256
|
await this.requireSession(address)
|
|
257
|
+
if (closeGeneration !== this.closeGeneration) {
|
|
258
|
+
return () => {}
|
|
259
|
+
}
|
|
251
260
|
const bound = address.toLowerCase()
|
|
252
|
-
boundHere = this.accountAddress !== bound
|
|
253
261
|
this.bindAddress(bound)
|
|
262
|
+
authGeneration = this.authGeneration
|
|
263
|
+
this.authBindingRefs += 1
|
|
254
264
|
}
|
|
265
|
+
const summaryGeneration =
|
|
266
|
+
sub.channel === 'accountSummary'
|
|
267
|
+
? this.accountSummaryGeneration
|
|
268
|
+
: undefined
|
|
269
|
+
let acquiredWireCount = 0
|
|
255
270
|
|
|
256
271
|
try {
|
|
257
272
|
if (needsLogin) {
|
|
@@ -259,32 +274,79 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
259
274
|
// market identity the mapped orders/fills/positions embed.
|
|
260
275
|
await this.registry?.sync()
|
|
261
276
|
}
|
|
262
|
-
if (
|
|
263
|
-
|
|
277
|
+
if (
|
|
278
|
+
authGeneration !== undefined &&
|
|
279
|
+
authGeneration !== this.authGeneration
|
|
280
|
+
) {
|
|
281
|
+
return () => {}
|
|
282
|
+
}
|
|
283
|
+
if (sub.channel === 'accountSummary' && summaryGeneration !== undefined) {
|
|
284
|
+
if (summaryGeneration !== this.accountSummaryGeneration) {
|
|
285
|
+
return () => {}
|
|
286
|
+
}
|
|
287
|
+
await this.seedAccountSummary(sub.address, summaryGeneration)
|
|
288
|
+
if (summaryGeneration !== this.accountSummaryGeneration) {
|
|
289
|
+
return () => {}
|
|
290
|
+
}
|
|
264
291
|
}
|
|
265
292
|
// Authenticated wire subs are shared and ref-counted; public ones are 1:1.
|
|
266
293
|
for (const [key, state] of wireSubs) {
|
|
294
|
+
state.authGeneration = authGeneration
|
|
267
295
|
await this.acquireWire(key, state, needsLogin)
|
|
296
|
+
acquiredWireCount += 1
|
|
297
|
+
if (
|
|
298
|
+
authGeneration !== undefined &&
|
|
299
|
+
authGeneration !== this.authGeneration
|
|
300
|
+
) {
|
|
301
|
+
return () => {}
|
|
302
|
+
}
|
|
268
303
|
}
|
|
269
304
|
await this.rws.ready()
|
|
305
|
+
if (
|
|
306
|
+
authGeneration !== undefined &&
|
|
307
|
+
authGeneration !== this.authGeneration
|
|
308
|
+
) {
|
|
309
|
+
return () => {}
|
|
310
|
+
}
|
|
270
311
|
} catch (err) {
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
312
|
+
// A failed opening releases only its own binding reservation.
|
|
313
|
+
if (
|
|
314
|
+
authGeneration === undefined ||
|
|
315
|
+
authGeneration === this.authGeneration
|
|
316
|
+
) {
|
|
317
|
+
if (summaryGeneration !== undefined) {
|
|
318
|
+
this.clearAccountSummary()
|
|
319
|
+
}
|
|
320
|
+
for (let index = 0; index < acquiredWireCount; index += 1) {
|
|
321
|
+
const [key, state] = wireSubs[index]
|
|
322
|
+
this.releaseWire(key, state, needsLogin)
|
|
323
|
+
}
|
|
324
|
+
if (authGeneration !== undefined) {
|
|
325
|
+
this.releaseBinding()
|
|
326
|
+
}
|
|
276
327
|
}
|
|
277
328
|
throw err
|
|
278
329
|
}
|
|
279
330
|
|
|
280
331
|
return () => {
|
|
281
|
-
if (
|
|
282
|
-
|
|
283
|
-
this.
|
|
332
|
+
if (
|
|
333
|
+
authGeneration !== undefined &&
|
|
334
|
+
authGeneration !== this.authGeneration
|
|
335
|
+
) {
|
|
336
|
+
return
|
|
337
|
+
}
|
|
338
|
+
if (summaryGeneration !== undefined) {
|
|
339
|
+
if (summaryGeneration !== this.accountSummaryGeneration) {
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
this.clearAccountSummary()
|
|
284
343
|
}
|
|
285
344
|
for (const [key, state] of wireSubs) {
|
|
286
345
|
this.releaseWire(key, state, needsLogin)
|
|
287
346
|
}
|
|
347
|
+
if (authGeneration !== undefined) {
|
|
348
|
+
this.releaseBinding()
|
|
349
|
+
}
|
|
288
350
|
}
|
|
289
351
|
}
|
|
290
352
|
|
|
@@ -324,14 +386,15 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
324
386
|
return true
|
|
325
387
|
}
|
|
326
388
|
|
|
327
|
-
/**
|
|
328
|
-
* Forget the authenticated binding and cycle the connection. The venue
|
|
329
|
-
* permits one login per socket, so a later subscribe for a different address
|
|
330
|
-
* must authenticate on a fresh connection rather than re-login on this one.
|
|
331
|
-
*/
|
|
389
|
+
/** Release a channel's binding reservation, including one still opening. */
|
|
332
390
|
private releaseBinding(): void {
|
|
391
|
+
this.authBindingRefs -= 1
|
|
392
|
+
if (this.authBindingRefs > 0) {
|
|
393
|
+
return
|
|
394
|
+
}
|
|
333
395
|
this.accountAddress = undefined
|
|
334
396
|
this.loginPromise = undefined
|
|
397
|
+
this.authGeneration += 1
|
|
335
398
|
this.rws.reconnect()
|
|
336
399
|
}
|
|
337
400
|
|
|
@@ -352,7 +415,9 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
352
415
|
await this.registerSub(key, state)
|
|
353
416
|
} catch (err) {
|
|
354
417
|
// Roll back the ref we just took so a later subscribe re-registers.
|
|
355
|
-
this.
|
|
418
|
+
if (state.authGeneration === this.authGeneration) {
|
|
419
|
+
this.authWireRefs.delete(key)
|
|
420
|
+
}
|
|
356
421
|
throw err
|
|
357
422
|
}
|
|
358
423
|
}
|
|
@@ -361,6 +426,9 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
361
426
|
/** Mirror of {@link acquireWire}: drop the wire sub on its last release. */
|
|
362
427
|
private releaseWire(key: string, state: SubState, shared: boolean): void {
|
|
363
428
|
if (shared) {
|
|
429
|
+
if (state.authGeneration !== this.authGeneration) {
|
|
430
|
+
return
|
|
431
|
+
}
|
|
364
432
|
const remaining = (this.authWireRefs.get(key) ?? 1) - 1
|
|
365
433
|
if (remaining > 0) {
|
|
366
434
|
this.authWireRefs.set(key, remaining)
|
|
@@ -370,19 +438,24 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
370
438
|
}
|
|
371
439
|
this.unregisterSub(key)
|
|
372
440
|
this.rws.send(JSON.stringify({ op: 'unsubscribe', ...state.frame }))
|
|
373
|
-
// Last authenticated wire gone: forget the binding and cycle the socket so
|
|
374
|
-
// the next address logs in on a fresh connection.
|
|
375
|
-
if (shared && this.authWireRefs.size === 0) {
|
|
376
|
-
this.releaseBinding()
|
|
377
|
-
}
|
|
378
441
|
}
|
|
379
442
|
|
|
380
443
|
protected async sendSubscribe(state: SubState): Promise<void> {
|
|
381
444
|
if (state.needsAuth && state.address !== undefined) {
|
|
445
|
+
if (state.authGeneration !== this.authGeneration) {
|
|
446
|
+
return
|
|
447
|
+
}
|
|
382
448
|
// Re-assert the binding: a replay after a reconnect (which cleared it on
|
|
383
449
|
// close) must restore the address account frames emit under.
|
|
384
450
|
this.accountAddress = state.address.toLowerCase()
|
|
385
|
-
|
|
451
|
+
const login = this.ensureLogin(state.address)
|
|
452
|
+
await login
|
|
453
|
+
if (
|
|
454
|
+
this.loginPromise !== login ||
|
|
455
|
+
state.authGeneration !== this.authGeneration
|
|
456
|
+
) {
|
|
457
|
+
return
|
|
458
|
+
}
|
|
386
459
|
}
|
|
387
460
|
this.rws.send(JSON.stringify({ op: 'subscribe', ...state.frame }))
|
|
388
461
|
}
|
|
@@ -392,6 +465,11 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
392
465
|
this.pendingFunding.clear()
|
|
393
466
|
this.loginPromise = undefined
|
|
394
467
|
this.accountAddress = undefined
|
|
468
|
+
this.authGeneration += 1
|
|
469
|
+
this.closeGeneration += 1
|
|
470
|
+
this.authWireRefs.clear()
|
|
471
|
+
this.authBindingRefs = 0
|
|
472
|
+
this.clearAccountSummary()
|
|
395
473
|
}
|
|
396
474
|
|
|
397
475
|
/** Wire subs for `sub`, keyed uniquely for the base's replay registry. */
|
|
@@ -523,14 +601,20 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
523
601
|
*/
|
|
524
602
|
private ensureLogin(address: Address): Promise<void> {
|
|
525
603
|
if (this.loginPromise === undefined) {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
604
|
+
const login: Promise<void> = this.requireSession(address).then(
|
|
605
|
+
(token) => {
|
|
606
|
+
if (this.loginPromise === login) {
|
|
607
|
+
this.rws.send(
|
|
608
|
+
JSON.stringify({ op: 'login', args: { token: token.token } })
|
|
609
|
+
)
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
)
|
|
613
|
+
this.loginPromise = login
|
|
614
|
+
login.catch(() => {
|
|
615
|
+
if (this.loginPromise === login) {
|
|
616
|
+
this.loginPromise = undefined
|
|
617
|
+
}
|
|
534
618
|
})
|
|
535
619
|
}
|
|
536
620
|
return this.loginPromise
|
|
@@ -548,24 +632,50 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
548
632
|
* OndoSessionExpiredError} when the SIWE session is missing, matching the
|
|
549
633
|
* other authenticated channels.
|
|
550
634
|
*/
|
|
551
|
-
private async seedAccountSummary(
|
|
635
|
+
private async seedAccountSummary(
|
|
636
|
+
address: Address,
|
|
637
|
+
generation: number
|
|
638
|
+
): Promise<void> {
|
|
552
639
|
const token = await this.requireSession(address)
|
|
553
|
-
|
|
554
|
-
|
|
640
|
+
if (generation !== this.accountSummaryGeneration) {
|
|
641
|
+
return
|
|
642
|
+
}
|
|
643
|
+
const balance = await this.readBalance(token.token)
|
|
644
|
+
if (generation !== this.accountSummaryGeneration) {
|
|
645
|
+
return
|
|
646
|
+
}
|
|
647
|
+
this.accountSummary = balance
|
|
648
|
+
this.emitAccountSummary(address)
|
|
555
649
|
}
|
|
556
650
|
|
|
557
651
|
/** Re-read the venue balance moved by the frame and re-emit the summary. */
|
|
558
|
-
private async refreshAccountSummary(
|
|
652
|
+
private async refreshAccountSummary(
|
|
653
|
+
address: Address,
|
|
654
|
+
generation: number
|
|
655
|
+
): Promise<void> {
|
|
559
656
|
const token = await this.tokenStore.get(address)
|
|
560
|
-
if (
|
|
657
|
+
if (
|
|
658
|
+
token === null ||
|
|
659
|
+
this.accountSummary === undefined ||
|
|
660
|
+
generation !== this.accountSummaryGeneration
|
|
661
|
+
) {
|
|
561
662
|
return
|
|
562
663
|
}
|
|
563
664
|
const balance = await this.readBalance(token.token)
|
|
564
|
-
if (
|
|
665
|
+
if (
|
|
666
|
+
this.accountSummary === undefined ||
|
|
667
|
+
generation !== this.accountSummaryGeneration
|
|
668
|
+
) {
|
|
565
669
|
return
|
|
566
670
|
}
|
|
567
671
|
this.accountSummary = balance
|
|
568
|
-
this.emitAccountSummary()
|
|
672
|
+
this.emitAccountSummary(address)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
private clearAccountSummary(): void {
|
|
676
|
+
this.accountSummaryGeneration += 1
|
|
677
|
+
this.accountSummary = undefined
|
|
678
|
+
this.accountSummaryChain.reset()
|
|
569
679
|
}
|
|
570
680
|
|
|
571
681
|
private async readBalance(authToken: string): Promise<OndoAccountBalance> {
|
|
@@ -584,13 +694,12 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
584
694
|
}
|
|
585
695
|
|
|
586
696
|
/** Emit the current account summary from the venue balance figures. */
|
|
587
|
-
private emitAccountSummary(): void {
|
|
697
|
+
private emitAccountSummary(address: Address): void {
|
|
588
698
|
const balance = this.accountSummary
|
|
589
|
-
|
|
590
|
-
if (balance === undefined || address === undefined) {
|
|
699
|
+
if (balance === undefined) {
|
|
591
700
|
return
|
|
592
701
|
}
|
|
593
|
-
this.emit(`accountSummary:${address}`, {
|
|
702
|
+
this.emit(`accountSummary:${address.toLowerCase()}`, {
|
|
594
703
|
channel: 'accountSummary',
|
|
595
704
|
data: {
|
|
596
705
|
portfolioValue: balance.marginBalance,
|
|
@@ -803,8 +912,9 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
803
912
|
}
|
|
804
913
|
this.emit(`fills:${address}`, { channel: 'fills', data: mapped })
|
|
805
914
|
if (this.accountSummary !== undefined) {
|
|
915
|
+
const generation = this.accountSummaryGeneration
|
|
806
916
|
this.accountSummaryChain.push(() =>
|
|
807
|
-
this.refreshAccountSummary(address as Address)
|
|
917
|
+
this.refreshAccountSummary(address as Address, generation)
|
|
808
918
|
)
|
|
809
919
|
}
|
|
810
920
|
}
|
|
@@ -829,8 +939,9 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
829
939
|
data: mapped,
|
|
830
940
|
})
|
|
831
941
|
if (this.accountSummary !== undefined) {
|
|
942
|
+
const generation = this.accountSummaryGeneration
|
|
832
943
|
this.accountSummaryChain.push(() =>
|
|
833
|
-
this.refreshAccountSummary(address as Address)
|
|
944
|
+
this.refreshAccountSummary(address as Address, generation)
|
|
834
945
|
)
|
|
835
946
|
}
|
|
836
947
|
}
|