@lifi/perps-sdk-provider-ondo 28.0.0 → 30.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/dist/cjs/OndoProvider.d.ts.map +1 -1
- package/dist/cjs/OndoProvider.js +4 -2
- package/dist/cjs/OndoProvider.js.map +1 -1
- package/dist/cjs/auth/signActions.d.ts +3 -3
- package/dist/cjs/auth/signActions.d.ts.map +1 -1
- package/dist/cjs/auth/signActions.js +67 -46
- package/dist/cjs/auth/signActions.js.map +1 -1
- package/dist/cjs/types/ws.d.ts +4 -0
- package/dist/cjs/types/ws.d.ts.map +1 -1
- package/dist/cjs/websocket/OndoWsProvider.d.ts +3 -0
- package/dist/cjs/websocket/OndoWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/OndoWsProvider.js +58 -16
- package/dist/cjs/websocket/OndoWsProvider.js.map +1 -1
- package/dist/esm/OndoProvider.d.ts.map +1 -1
- package/dist/esm/OndoProvider.js +9 -6
- package/dist/esm/OndoProvider.js.map +1 -1
- package/dist/esm/auth/signActions.d.ts +11 -6
- package/dist/esm/auth/signActions.d.ts.map +1 -1
- package/dist/esm/auth/signActions.js +79 -53
- package/dist/esm/auth/signActions.js.map +1 -1
- package/dist/esm/types/ws.d.ts +4 -0
- package/dist/esm/types/ws.d.ts.map +1 -1
- package/dist/esm/websocket/OndoWsProvider.d.ts +5 -0
- package/dist/esm/websocket/OndoWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/OndoWsProvider.js +66 -21
- package/dist/esm/websocket/OndoWsProvider.js.map +1 -1
- package/dist/types/OndoProvider.d.ts.map +1 -1
- package/dist/types/auth/signActions.d.ts +11 -6
- package/dist/types/auth/signActions.d.ts.map +1 -1
- package/dist/types/types/ws.d.ts +4 -0
- package/dist/types/types/ws.d.ts.map +1 -1
- package/dist/types/websocket/OndoWsProvider.d.ts +5 -0
- package/dist/types/websocket/OndoWsProvider.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/OndoProvider.ts +11 -6
- package/src/auth/signActions.ts +116 -81
- package/src/types/ws.ts +1 -0
- package/src/websocket/OndoWsProvider.ts +93 -36
package/src/auth/signActions.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
PerpsError,
|
|
3
|
+
rethrowWithKnownResults,
|
|
4
|
+
type SignActionsContext,
|
|
5
|
+
type SignActionsResult,
|
|
6
|
+
} from '@lifi/perps-sdk'
|
|
2
7
|
import type {
|
|
8
|
+
ActionResult,
|
|
3
9
|
ActionStep,
|
|
4
10
|
HmacActionStep,
|
|
5
11
|
HmacSignedActionStep,
|
|
6
12
|
SessionActionStep,
|
|
7
|
-
SignedActionStep,
|
|
8
13
|
SiweActionStep,
|
|
9
14
|
} from '@lifi/perps-types'
|
|
10
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
ActionType,
|
|
17
|
+
PerpsErrorCode,
|
|
18
|
+
SigningMethod,
|
|
19
|
+
UserActionStatus,
|
|
20
|
+
} from '@lifi/perps-types'
|
|
11
21
|
import { type Address, getAddress } from 'viem'
|
|
12
22
|
import {
|
|
13
23
|
ONDO_API_KEY_NAME,
|
|
@@ -249,12 +259,17 @@ async function executeSessionRequest(
|
|
|
249
259
|
* `SIWE` signs the backend-built ERC-4361 challenge with the user's wallet and
|
|
250
260
|
* completes the login directly against Ondo. The session JWT is persisted in
|
|
251
261
|
* the token store and no signed step is returned, so neither the JWT nor the
|
|
252
|
-
* wallet signature transits the LI.FI backend.
|
|
262
|
+
* wallet signature transits the LI.FI backend. Each completed login is an
|
|
263
|
+
* `Accepted` executed result.
|
|
253
264
|
*
|
|
254
265
|
* `SESSION` executes client-only setup steps directly against the venue with
|
|
255
266
|
* the stored session token. A backend-authored request-bearing step is
|
|
256
|
-
* dispatched as-is; a bare marker step is keyed on its action.
|
|
257
|
-
*
|
|
267
|
+
* dispatched as-is; a bare marker step is keyed on its action. Each step the
|
|
268
|
+
* venue answered is an `Accepted` executed result, and no signed step is
|
|
269
|
+
* returned, so `executeAction` is skipped.
|
|
270
|
+
*
|
|
271
|
+
* A step that throws after an earlier step executed throws a
|
|
272
|
+
* `PerpsExecutionError` that holds the earlier results.
|
|
258
273
|
*
|
|
259
274
|
* `HMAC` computes a per-request HMAC-SHA256 signature over each request step
|
|
260
275
|
* from the client-held API key (creating one on first use), attaching the
|
|
@@ -269,29 +284,35 @@ export async function ondoSignActions(
|
|
|
269
284
|
steps: ActionStep[],
|
|
270
285
|
address: Address,
|
|
271
286
|
ctx?: SignActionsContext
|
|
272
|
-
): Promise<
|
|
287
|
+
): Promise<SignActionsResult> {
|
|
273
288
|
switch (method) {
|
|
274
289
|
case SigningMethod.SIWE: {
|
|
275
290
|
const userWallet = requireUserWallet(ctx)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
291
|
+
const executed: ActionResult[] = []
|
|
292
|
+
try {
|
|
293
|
+
for (const step of steps) {
|
|
294
|
+
if (!isSiweStep(step)) {
|
|
295
|
+
throw new PerpsError(
|
|
296
|
+
PerpsErrorCode.SDKError,
|
|
297
|
+
`Ondo received a non-SIWE step ('${step.action}') under the SIWE signing method.`
|
|
298
|
+
)
|
|
299
|
+
}
|
|
300
|
+
const token = await completeSiweLogin(deps.client, userWallet, {
|
|
301
|
+
id: step.siwe.challengeId,
|
|
302
|
+
message: step.siwe.message,
|
|
303
|
+
})
|
|
304
|
+
executed.push(acceptedResult(step))
|
|
305
|
+
await deps.tokenStore.set(address, token)
|
|
282
306
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
message: step.siwe.message,
|
|
286
|
-
})
|
|
287
|
-
await deps.tokenStore.set(address, token)
|
|
307
|
+
} catch (error) {
|
|
308
|
+
rethrowWithKnownResults(executed, error)
|
|
288
309
|
}
|
|
289
|
-
return []
|
|
310
|
+
return { signed: [], executed }
|
|
290
311
|
}
|
|
291
312
|
|
|
292
313
|
case SigningMethod.HMAC: {
|
|
293
314
|
const apiKey = await ensureApiKey(deps, address)
|
|
294
|
-
|
|
315
|
+
const signed = await Promise.all(
|
|
295
316
|
steps.map(async (step): Promise<HmacSignedActionStep> => {
|
|
296
317
|
if (!hasRequest(step)) {
|
|
297
318
|
throw new PerpsError(
|
|
@@ -318,72 +339,20 @@ export async function ondoSignActions(
|
|
|
318
339
|
}
|
|
319
340
|
})
|
|
320
341
|
)
|
|
342
|
+
return { signed, executed: [] }
|
|
321
343
|
}
|
|
322
344
|
|
|
323
345
|
case SigningMethod.SESSION: {
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
if (!isSessionStep(step)) {
|
|
330
|
-
throw new PerpsError(
|
|
331
|
-
PerpsErrorCode.SDKError,
|
|
332
|
-
`Ondo received a non-session step ('${step.action}') under the session signing method.`
|
|
333
|
-
)
|
|
334
|
-
}
|
|
335
|
-
switch (step.action) {
|
|
336
|
-
case ActionType.CREATE_DEPOSIT_ADDRESS: {
|
|
337
|
-
const token = await requireOndoSessionToken(
|
|
338
|
-
deps.tokenStore,
|
|
339
|
-
address
|
|
340
|
-
)
|
|
341
|
-
const account = await deps.client.get<{ accountID?: unknown }>(
|
|
342
|
-
'/v1/account',
|
|
343
|
-
{ authToken: token.token }
|
|
344
|
-
)
|
|
345
|
-
const payload = buildOndoProvisionPayload(
|
|
346
|
-
step.session,
|
|
347
|
-
typeof account.accountID === 'string' ? account.accountID : ''
|
|
348
|
-
)
|
|
349
|
-
await deps.client.post('/v1/provision_address', payload, {
|
|
350
|
-
authToken: token.token,
|
|
351
|
-
})
|
|
352
|
-
// Re-query to validate that Ondo can read the provisioned address;
|
|
353
|
-
// getAccount performs the authoritative state refresh afterward.
|
|
354
|
-
await listOndoDepositAddress(deps.client, token.token)
|
|
355
|
-
break
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
case ActionType.ACCEPT_PROVIDER_TERMS: {
|
|
359
|
-
const token = await requireOndoSessionToken(
|
|
360
|
-
deps.tokenStore,
|
|
361
|
-
address
|
|
362
|
-
)
|
|
363
|
-
await deps.client.post(
|
|
364
|
-
'/v1/agreement',
|
|
365
|
-
{
|
|
366
|
-
termsVersion: ONDO_TERMS_VERSION,
|
|
367
|
-
privacyVersion: ONDO_PRIVACY_VERSION,
|
|
368
|
-
},
|
|
369
|
-
{ authToken: token.token }
|
|
370
|
-
)
|
|
371
|
-
break
|
|
372
|
-
}
|
|
373
|
-
case ActionType.REGISTER_API_KEY:
|
|
374
|
-
await ensureApiKey(deps, address)
|
|
375
|
-
break
|
|
376
|
-
case ActionType.ADD_WITHDRAWAL_ADDRESS:
|
|
377
|
-
await addWithdrawalAddress(deps, address, ctx)
|
|
378
|
-
break
|
|
379
|
-
default:
|
|
380
|
-
throw new PerpsError(
|
|
381
|
-
PerpsErrorCode.SDKError,
|
|
382
|
-
`Ondo has no session-step executor for action '${step.action}'.`
|
|
383
|
-
)
|
|
346
|
+
const executed: ActionResult[] = []
|
|
347
|
+
try {
|
|
348
|
+
for (const step of steps) {
|
|
349
|
+
await executeSessionStep(deps, address, step, ctx)
|
|
350
|
+
executed.push(acceptedResult(step))
|
|
384
351
|
}
|
|
352
|
+
} catch (error) {
|
|
353
|
+
rethrowWithKnownResults(executed, error)
|
|
385
354
|
}
|
|
386
|
-
return []
|
|
355
|
+
return { signed: [], executed }
|
|
387
356
|
}
|
|
388
357
|
|
|
389
358
|
default:
|
|
@@ -393,3 +362,69 @@ export async function ondoSignActions(
|
|
|
393
362
|
)
|
|
394
363
|
}
|
|
395
364
|
}
|
|
365
|
+
|
|
366
|
+
function acceptedResult(step: ActionStep): ActionResult {
|
|
367
|
+
return { action: step.action, status: UserActionStatus.Accepted }
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function executeSessionStep(
|
|
371
|
+
deps: OndoSignActionsDeps,
|
|
372
|
+
address: Address,
|
|
373
|
+
step: ActionStep,
|
|
374
|
+
ctx: SignActionsContext | undefined
|
|
375
|
+
): Promise<void> {
|
|
376
|
+
if (hasRequest(step)) {
|
|
377
|
+
await executeSessionRequest(deps, address, step.request)
|
|
378
|
+
return
|
|
379
|
+
}
|
|
380
|
+
if (!isSessionStep(step)) {
|
|
381
|
+
throw new PerpsError(
|
|
382
|
+
PerpsErrorCode.SDKError,
|
|
383
|
+
`Ondo received a non-session step ('${step.action}') under the session signing method.`
|
|
384
|
+
)
|
|
385
|
+
}
|
|
386
|
+
switch (step.action) {
|
|
387
|
+
case ActionType.CREATE_DEPOSIT_ADDRESS: {
|
|
388
|
+
const token = await requireOndoSessionToken(deps.tokenStore, address)
|
|
389
|
+
const account = await deps.client.get<{ accountID?: unknown }>(
|
|
390
|
+
'/v1/account',
|
|
391
|
+
{ authToken: token.token }
|
|
392
|
+
)
|
|
393
|
+
const payload = buildOndoProvisionPayload(
|
|
394
|
+
step.session,
|
|
395
|
+
typeof account.accountID === 'string' ? account.accountID : ''
|
|
396
|
+
)
|
|
397
|
+
await deps.client.post('/v1/provision_address', payload, {
|
|
398
|
+
authToken: token.token,
|
|
399
|
+
})
|
|
400
|
+
// Re-query to validate that Ondo can read the provisioned address;
|
|
401
|
+
// getAccount performs the authoritative state refresh afterward.
|
|
402
|
+
await listOndoDepositAddress(deps.client, token.token)
|
|
403
|
+
break
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
case ActionType.ACCEPT_PROVIDER_TERMS: {
|
|
407
|
+
const token = await requireOndoSessionToken(deps.tokenStore, address)
|
|
408
|
+
await deps.client.post(
|
|
409
|
+
'/v1/agreement',
|
|
410
|
+
{
|
|
411
|
+
termsVersion: ONDO_TERMS_VERSION,
|
|
412
|
+
privacyVersion: ONDO_PRIVACY_VERSION,
|
|
413
|
+
},
|
|
414
|
+
{ authToken: token.token }
|
|
415
|
+
)
|
|
416
|
+
break
|
|
417
|
+
}
|
|
418
|
+
case ActionType.REGISTER_API_KEY:
|
|
419
|
+
await ensureApiKey(deps, address)
|
|
420
|
+
break
|
|
421
|
+
case ActionType.ADD_WITHDRAWAL_ADDRESS:
|
|
422
|
+
await addWithdrawalAddress(deps, address, ctx)
|
|
423
|
+
break
|
|
424
|
+
default:
|
|
425
|
+
throw new PerpsError(
|
|
426
|
+
PerpsErrorCode.SDKError,
|
|
427
|
+
`Ondo has no session-step executor for action '${step.action}'.`
|
|
428
|
+
)
|
|
429
|
+
}
|
|
430
|
+
}
|
package/src/types/ws.ts
CHANGED
|
@@ -65,6 +65,7 @@ export interface OndoFundingRate {
|
|
|
65
65
|
/** Server frame envelope. Non-`update` types are acks or errors. @public */
|
|
66
66
|
export type OndoWsMessage =
|
|
67
67
|
| { type: 'update'; channel: 'depthBooksPerps'; data: OndoBookSnapshot[] }
|
|
68
|
+
| { type: 'update'; channel: 'topOfBooksPerps'; data: OndoBookSnapshot[] }
|
|
68
69
|
| { type: 'update'; channel: 'tradesPerps'; data: OndoWsTrade[] }
|
|
69
70
|
| { type: 'update'; channel: 'kLinePerps'; data: OndoKline }
|
|
70
71
|
| { type: 'update'; channel: 'markPricesPerps'; data: OndoMarkPrice[] }
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
addDecimalString,
|
|
2
3
|
DecodeChain,
|
|
4
|
+
divideDecimalString,
|
|
3
5
|
getMarketRegistry,
|
|
4
6
|
isDecimalString,
|
|
5
7
|
localStorageAdapter,
|
|
@@ -40,6 +42,7 @@ import {
|
|
|
40
42
|
import type {
|
|
41
43
|
OndoAuthToken,
|
|
42
44
|
OndoBalanceSummary,
|
|
45
|
+
OndoBookLevel,
|
|
43
46
|
OndoBookSnapshot,
|
|
44
47
|
OndoFill,
|
|
45
48
|
OndoFundingRate,
|
|
@@ -62,9 +65,10 @@ import { intervalFromBarSpan, mapInterval } from '../utils/ohlcvInterval.js'
|
|
|
62
65
|
|
|
63
66
|
// Public channels (per market, demuxed from the frame's `market` field):
|
|
64
67
|
// orderbook → depthBooksPerps, trades → tradesPerps, candle → kLinePerps.
|
|
65
|
-
// `marketsContext`/`marketContext` merge
|
|
66
|
-
// and
|
|
67
|
-
//
|
|
68
|
+
// `marketsContext`/`marketContext` merge three wire channels — markPricesPerps,
|
|
69
|
+
// fundingRatesPerps and topOfBooksPerps — into the aggregated context emit.
|
|
70
|
+
// `midPrice` is the top-of-book mid; the mark price stands in until a
|
|
71
|
+
// two-sided book arrives for the market.
|
|
68
72
|
// Authenticated channels (account-wide, no market filter):
|
|
69
73
|
// orderUpdates → ordersPerps, fills → fillsPerps, positions → positionsPerps.
|
|
70
74
|
//
|
|
@@ -166,6 +170,8 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
166
170
|
string,
|
|
167
171
|
{ rate: string; nextFundingTime: number }
|
|
168
172
|
>()
|
|
173
|
+
/** Latest two-sided top-of-book mid, keyed by venue market symbol. */
|
|
174
|
+
private bookMids = new Map<string, string>()
|
|
169
175
|
|
|
170
176
|
/** The single address this connection's login is bound to. */
|
|
171
177
|
private accountAddress: string | undefined
|
|
@@ -472,6 +478,7 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
472
478
|
protected override onClose(): void {
|
|
473
479
|
this.contexts = {}
|
|
474
480
|
this.pendingFunding.clear()
|
|
481
|
+
this.bookMids.clear()
|
|
475
482
|
this.loginPromise = undefined
|
|
476
483
|
this.accountAddress = undefined
|
|
477
484
|
this.authGeneration += 1
|
|
@@ -494,6 +501,10 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
494
501
|
'fundingRatesPerps',
|
|
495
502
|
{ frame: { channel: 'fundingRatesPerps' }, needsAuth: false },
|
|
496
503
|
],
|
|
504
|
+
[
|
|
505
|
+
'topOfBooksPerps',
|
|
506
|
+
{ frame: { channel: 'topOfBooksPerps' }, needsAuth: false },
|
|
507
|
+
],
|
|
497
508
|
]
|
|
498
509
|
case 'marketContext':
|
|
499
510
|
return [
|
|
@@ -511,6 +522,13 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
511
522
|
needsAuth: false,
|
|
512
523
|
},
|
|
513
524
|
],
|
|
525
|
+
[
|
|
526
|
+
`topOfBooksPerps:${sub.marketId}`,
|
|
527
|
+
{
|
|
528
|
+
frame: { channel: 'topOfBooksPerps', markets: [sub.marketId] },
|
|
529
|
+
needsAuth: false,
|
|
530
|
+
},
|
|
531
|
+
],
|
|
514
532
|
]
|
|
515
533
|
case 'orderbook':
|
|
516
534
|
return [
|
|
@@ -758,6 +776,9 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
758
776
|
case 'depthBooksPerps':
|
|
759
777
|
this.handleBooks(msg.data as OndoBookSnapshot[])
|
|
760
778
|
break
|
|
779
|
+
case 'topOfBooksPerps':
|
|
780
|
+
this.handleTopOfBooks(msg.data as OndoBookSnapshot[])
|
|
781
|
+
break
|
|
761
782
|
case 'tradesPerps':
|
|
762
783
|
this.handleTrades(msg.data as OndoWsTrade[])
|
|
763
784
|
break
|
|
@@ -785,50 +806,86 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
785
806
|
}
|
|
786
807
|
}
|
|
787
808
|
|
|
809
|
+
/** Valid levels of one book side, best first (`-1` bids, `1` asks). */
|
|
810
|
+
private toBookLevels(
|
|
811
|
+
market: string,
|
|
812
|
+
levels: OndoBookLevel[],
|
|
813
|
+
direction: 1 | -1
|
|
814
|
+
) {
|
|
815
|
+
return levels
|
|
816
|
+
.flatMap(([price, size]) => {
|
|
817
|
+
if (!isDecimalString(price)) {
|
|
818
|
+
wsLog.skippedRow(
|
|
819
|
+
this.providerKey,
|
|
820
|
+
BOOK_LEVEL_ROW,
|
|
821
|
+
'price',
|
|
822
|
+
price,
|
|
823
|
+
market
|
|
824
|
+
)
|
|
825
|
+
return []
|
|
826
|
+
}
|
|
827
|
+
if (!isDecimalString(size)) {
|
|
828
|
+
wsLog.skippedRow(
|
|
829
|
+
this.providerKey,
|
|
830
|
+
BOOK_LEVEL_ROW,
|
|
831
|
+
'size',
|
|
832
|
+
size,
|
|
833
|
+
market
|
|
834
|
+
)
|
|
835
|
+
return []
|
|
836
|
+
}
|
|
837
|
+
return [{ price, size }]
|
|
838
|
+
})
|
|
839
|
+
.sort(
|
|
840
|
+
(a, b) => direction * (safeCompareDecimalStrings(a.price, b.price) ?? 0)
|
|
841
|
+
)
|
|
842
|
+
}
|
|
843
|
+
|
|
788
844
|
private handleBooks(snapshots: OndoBookSnapshot[]): void {
|
|
789
845
|
for (const snap of snapshots) {
|
|
790
|
-
const toLevels = (levels: OndoBookSnapshot['bids'], direction: 1 | -1) =>
|
|
791
|
-
levels
|
|
792
|
-
.flatMap(([price, size]) => {
|
|
793
|
-
if (!isDecimalString(price)) {
|
|
794
|
-
wsLog.skippedRow(
|
|
795
|
-
this.providerKey,
|
|
796
|
-
BOOK_LEVEL_ROW,
|
|
797
|
-
'price',
|
|
798
|
-
price,
|
|
799
|
-
snap.market
|
|
800
|
-
)
|
|
801
|
-
return []
|
|
802
|
-
}
|
|
803
|
-
if (!isDecimalString(size)) {
|
|
804
|
-
wsLog.skippedRow(
|
|
805
|
-
this.providerKey,
|
|
806
|
-
BOOK_LEVEL_ROW,
|
|
807
|
-
'size',
|
|
808
|
-
size,
|
|
809
|
-
snap.market
|
|
810
|
-
)
|
|
811
|
-
return []
|
|
812
|
-
}
|
|
813
|
-
return [{ price, size }]
|
|
814
|
-
})
|
|
815
|
-
.sort(
|
|
816
|
-
(a, b) =>
|
|
817
|
-
direction * (safeCompareDecimalStrings(a.price, b.price) ?? 0)
|
|
818
|
-
)
|
|
819
846
|
this.emit(`orderbook:${snap.market}`, {
|
|
820
847
|
channel: 'orderbook',
|
|
821
848
|
data: {
|
|
822
849
|
provider: this.providerKey,
|
|
823
850
|
marketId: snap.market,
|
|
824
|
-
bids:
|
|
825
|
-
asks:
|
|
851
|
+
bids: this.toBookLevels(snap.market, snap.bids, -1),
|
|
852
|
+
asks: this.toBookLevels(snap.market, snap.asks, 1),
|
|
826
853
|
timestamp: Date.parse(snap.time),
|
|
827
854
|
},
|
|
828
855
|
})
|
|
829
856
|
}
|
|
830
857
|
}
|
|
831
858
|
|
|
859
|
+
private handleTopOfBooks(snapshots: OndoBookSnapshot[]): void {
|
|
860
|
+
const touched: string[] = []
|
|
861
|
+
for (const { market, bids, asks } of snapshots) {
|
|
862
|
+
const bestBid = this.toBookLevels(market, bids, -1)[0]
|
|
863
|
+
const bestAsk = this.toBookLevels(market, asks, 1)[0]
|
|
864
|
+
if (bestBid === undefined || bestAsk === undefined) {
|
|
865
|
+
this.bookMids.delete(market)
|
|
866
|
+
} else {
|
|
867
|
+
this.bookMids.set(
|
|
868
|
+
market,
|
|
869
|
+
divideDecimalString(
|
|
870
|
+
addDecimalString(bestBid.price, bestAsk.price),
|
|
871
|
+
'2'
|
|
872
|
+
)
|
|
873
|
+
)
|
|
874
|
+
}
|
|
875
|
+
const context = this.contexts[market]
|
|
876
|
+
if (context !== undefined) {
|
|
877
|
+
this.contexts[market] = {
|
|
878
|
+
...context,
|
|
879
|
+
midPrice: this.bookMids.get(market) ?? context.markPrice,
|
|
880
|
+
}
|
|
881
|
+
touched.push(market)
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (touched.length > 0) {
|
|
885
|
+
this.emitContexts(touched)
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
|
|
832
889
|
private handleTrades(trades: OndoWsTrade[]): void {
|
|
833
890
|
const byMarket = new Map<string, OndoWsTrade[]>()
|
|
834
891
|
for (const trade of trades) {
|
|
@@ -878,8 +935,8 @@ export class OndoWsProvider extends WsProviderBase<SubState> {
|
|
|
878
935
|
const context: MarketContext = {
|
|
879
936
|
...this.contexts[market],
|
|
880
937
|
marketId: market,
|
|
881
|
-
//
|
|
882
|
-
midPrice: markPrice,
|
|
938
|
+
// The mark price stands in until a two-sided top-of-book arrives.
|
|
939
|
+
midPrice: this.bookMids.get(market) ?? markPrice,
|
|
883
940
|
markPrice,
|
|
884
941
|
oraclePrice,
|
|
885
942
|
}
|