@varla/sdk 2.16.0 → 2.18.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.
@@ -275,6 +275,22 @@ export type ReadOracleTiming = {
275
275
  lastUpdated: bigint;
276
276
  };
277
277
 
278
+ export type ReadOracleDashboardRow = {
279
+ positionId: bigint;
280
+ snapshot: ReadPositionSnapshot;
281
+ meta: ReadOraclePositionMeta;
282
+ resolvedStatus: { positionId: bigint; isResolved: boolean };
283
+ staleStatus: { positionId: bigint; isPriceStale: boolean };
284
+ priceData: ReadOraclePriceData;
285
+ };
286
+
287
+ export type ReadOracleDashboard = {
288
+ config: ReadOracleConfig;
289
+ basics: ReadOracleBasics;
290
+ positionIds: bigint[];
291
+ rows: ReadOracleDashboardRow[];
292
+ };
293
+
278
294
  // wraps: VarlaOracle.getPriceData
279
295
  export async function readOraclePriceData(params: {
280
296
  oracle: {
@@ -368,6 +384,302 @@ export async function readOracleTiming(params: {
368
384
  };
369
385
  }
370
386
 
387
+ /**
388
+ * Multicall-chunked collateral status hydration for many positions.
389
+ */
390
+ // wraps: VarlaOracle.isConfigured,VarlaOracle.isResolved,VarlaOracle.isDepositAllowed,VarlaOracle.isValidCollateral,VarlaOracle.isPriceStale
391
+ export async function readManyOracleCollateralStatus(params: {
392
+ oracle: { address: Address };
393
+ client: { multicall: (args: any) => Promise<any> };
394
+ positionIds: readonly bigint[];
395
+ chunkSize?: number;
396
+ }): Promise<ReadOracleCollateralStatus[]> {
397
+ const chunkSize = params.chunkSize ?? 256;
398
+ if (params.positionIds.length === 0) return [];
399
+
400
+ const calls = params.positionIds.flatMap((pid) => [
401
+ {
402
+ address: params.oracle.address,
403
+ abi: abis.VARLAORACLE_ABI,
404
+ functionName: "isConfigured" as const,
405
+ args: [pid] as const,
406
+ },
407
+ {
408
+ address: params.oracle.address,
409
+ abi: abis.VARLAORACLE_ABI,
410
+ functionName: "isResolved" as const,
411
+ args: [pid] as const,
412
+ },
413
+ {
414
+ address: params.oracle.address,
415
+ abi: abis.VARLAORACLE_ABI,
416
+ functionName: "isDepositAllowed" as const,
417
+ args: [pid] as const,
418
+ },
419
+ {
420
+ address: params.oracle.address,
421
+ abi: abis.VARLAORACLE_ABI,
422
+ functionName: "isValidCollateral" as const,
423
+ args: [pid] as const,
424
+ },
425
+ {
426
+ address: params.oracle.address,
427
+ abi: abis.VARLAORACLE_ABI,
428
+ functionName: "isPriceStale" as const,
429
+ args: [pid] as const,
430
+ },
431
+ ]);
432
+
433
+ const res = await multicallChunks({
434
+ client: params.client as any,
435
+ contracts: calls as any,
436
+ chunkSize,
437
+ });
438
+
439
+ const out: ReadOracleCollateralStatus[] = [];
440
+ const stride = 5;
441
+ for (let i = 0; i < params.positionIds.length; i++) {
442
+ const base = i * stride;
443
+ const r0: any = (res as any[])[base + 0];
444
+ const r1: any = (res as any[])[base + 1];
445
+ const r2: any = (res as any[])[base + 2];
446
+ const r3: any = (res as any[])[base + 3];
447
+ const r4: any = (res as any[])[base + 4];
448
+ if (
449
+ r0.status !== "success" ||
450
+ r1.status !== "success" ||
451
+ r2.status !== "success" ||
452
+ r3.status !== "success" ||
453
+ r4.status !== "success"
454
+ ) {
455
+ throw new Error("Oracle collateral status multicall failed");
456
+ }
457
+ out.push({
458
+ positionId: params.positionIds[i]!,
459
+ isConfigured: r0.result as boolean,
460
+ isResolved: r1.result as boolean,
461
+ isDepositAllowed: r2.result as boolean,
462
+ isValidCollateral: r3.result as boolean,
463
+ isPriceStale: r4.result as boolean,
464
+ });
465
+ }
466
+ return out;
467
+ }
468
+
469
+ // wraps: VarlaOracle.isResolved,VarlaOracle.isPriceStale
470
+ export async function readManyOracleStatusFlags(params: {
471
+ oracle: { address: Address };
472
+ client: { multicall: (args: any) => Promise<any> };
473
+ positionIds: readonly bigint[];
474
+ chunkSize?: number;
475
+ }): Promise<Array<{ positionId: bigint; isResolved: boolean; isPriceStale: boolean }>> {
476
+ const chunkSize = params.chunkSize ?? 256;
477
+ if (params.positionIds.length === 0) return [];
478
+
479
+ const calls = params.positionIds.flatMap((pid) => [
480
+ {
481
+ address: params.oracle.address,
482
+ abi: abis.VARLAORACLE_ABI,
483
+ functionName: "isResolved" as const,
484
+ args: [pid] as const,
485
+ },
486
+ {
487
+ address: params.oracle.address,
488
+ abi: abis.VARLAORACLE_ABI,
489
+ functionName: "isPriceStale" as const,
490
+ args: [pid] as const,
491
+ },
492
+ ]);
493
+
494
+ const res = await multicallChunks({
495
+ client: params.client as any,
496
+ contracts: calls as any,
497
+ chunkSize,
498
+ });
499
+
500
+ const out: Array<{ positionId: bigint; isResolved: boolean; isPriceStale: boolean }> = [];
501
+ const stride = 2;
502
+ for (let i = 0; i < params.positionIds.length; i++) {
503
+ const base = i * stride;
504
+ const r0: any = (res as any[])[base + 0];
505
+ const r1: any = (res as any[])[base + 1];
506
+ if (r0.status !== "success" || r1.status !== "success") {
507
+ throw new Error("Oracle status flags multicall failed");
508
+ }
509
+ out.push({
510
+ positionId: params.positionIds[i]!,
511
+ isResolved: r0.result as boolean,
512
+ isPriceStale: r1.result as boolean,
513
+ });
514
+ }
515
+ return out;
516
+ }
517
+
518
+ /**
519
+ * Multicall-chunked timing hydration for many positions.
520
+ */
521
+ // wraps: VarlaOracle.getEarlyClosureWindow,VarlaOracle.getEarlyClosureFactor,VarlaOracle.lastRecoveryFromStale,VarlaOracle.getLastUpdated
522
+ export async function readManyOracleTiming(params: {
523
+ oracle: { address: Address };
524
+ client: { multicall: (args: any) => Promise<any> };
525
+ positionIds: readonly bigint[];
526
+ chunkSize?: number;
527
+ }): Promise<ReadOracleTiming[]> {
528
+ const chunkSize = params.chunkSize ?? 256;
529
+ if (params.positionIds.length === 0) return [];
530
+
531
+ const calls = params.positionIds.flatMap((pid) => [
532
+ {
533
+ address: params.oracle.address,
534
+ abi: abis.VARLAORACLE_ABI,
535
+ functionName: "getEarlyClosureWindow" as const,
536
+ args: [pid] as const,
537
+ },
538
+ {
539
+ address: params.oracle.address,
540
+ abi: abis.VARLAORACLE_ABI,
541
+ functionName: "getEarlyClosureFactor" as const,
542
+ args: [pid] as const,
543
+ },
544
+ {
545
+ address: params.oracle.address,
546
+ abi: abis.VARLAORACLE_ABI,
547
+ functionName: "lastRecoveryFromStale" as const,
548
+ args: [pid] as const,
549
+ },
550
+ {
551
+ address: params.oracle.address,
552
+ abi: abis.VARLAORACLE_ABI,
553
+ functionName: "getLastUpdated" as const,
554
+ args: [pid] as const,
555
+ },
556
+ ]);
557
+
558
+ const res = await multicallChunks({
559
+ client: params.client as any,
560
+ contracts: calls as any,
561
+ chunkSize,
562
+ });
563
+
564
+ const out: ReadOracleTiming[] = [];
565
+ const stride = 4;
566
+ for (let i = 0; i < params.positionIds.length; i++) {
567
+ const base = i * stride;
568
+ const r0: any = (res as any[])[base + 0];
569
+ const r1: any = (res as any[])[base + 1];
570
+ const r2: any = (res as any[])[base + 2];
571
+ const r3: any = (res as any[])[base + 3];
572
+ if (
573
+ r0.status !== "success" ||
574
+ r1.status !== "success" ||
575
+ r2.status !== "success" ||
576
+ r3.status !== "success"
577
+ ) {
578
+ throw new Error("Oracle timing multicall failed");
579
+ }
580
+ out.push({
581
+ positionId: params.positionIds[i]!,
582
+ earlyClosureWindow: r0.result as bigint,
583
+ earlyClosureFactorWad: r1.result as bigint,
584
+ lastRecoveryFromStale: r2.result as bigint,
585
+ lastUpdated: r3.result as bigint,
586
+ });
587
+ }
588
+ return out;
589
+ }
590
+
591
+ /**
592
+ * Multicall-chunked tryGetPrice hydration for many positions.
593
+ */
594
+ // wraps: VarlaOracle.tryGetPrice
595
+ export async function readManyTryGetPrice(params: {
596
+ oracle: { address: Address };
597
+ client: { multicall: (args: any) => Promise<any> };
598
+ positionIds: readonly bigint[];
599
+ chunkSize?: number;
600
+ }): Promise<ReadTryGetPrice[]> {
601
+ const chunkSize = params.chunkSize ?? 256;
602
+ if (params.positionIds.length === 0) return [];
603
+
604
+ const calls = params.positionIds.map((pid) => ({
605
+ address: params.oracle.address,
606
+ abi: abis.VARLAORACLE_ABI,
607
+ functionName: "tryGetPrice" as const,
608
+ args: [pid] as const,
609
+ }));
610
+
611
+ const res = await multicallChunks({
612
+ client: params.client as any,
613
+ contracts: calls as any,
614
+ chunkSize,
615
+ });
616
+
617
+ const out: ReadTryGetPrice[] = [];
618
+ for (let i = 0; i < res.length; i++) {
619
+ const r: any = (res as any[])[i];
620
+ if (r.status !== "success") {
621
+ throw new Error(`Oracle tryGetPrice multicall failed: ${String(r.error ?? "unknown")}`);
622
+ }
623
+ out.push(
624
+ await readTryGetPrice({
625
+ oracle: {
626
+ read: {
627
+ tryGetPrice: async () => r.result,
628
+ },
629
+ } as any,
630
+ positionId: params.positionIds[i]!,
631
+ }),
632
+ );
633
+ }
634
+ return out;
635
+ }
636
+
637
+ /**
638
+ * Multicall-chunked price data hydration for many positions.
639
+ */
640
+ // wraps: VarlaOracle.getPriceData
641
+ export async function readManyOraclePriceData(params: {
642
+ oracle: { address: Address };
643
+ client: { multicall: (args: any) => Promise<any> };
644
+ positionIds: readonly bigint[];
645
+ chunkSize?: number;
646
+ }): Promise<ReadOraclePriceData[]> {
647
+ const chunkSize = params.chunkSize ?? 256;
648
+ if (params.positionIds.length === 0) return [];
649
+
650
+ const calls = params.positionIds.map((pid) => ({
651
+ address: params.oracle.address,
652
+ abi: abis.VARLAORACLE_ABI,
653
+ functionName: "getPriceData" as const,
654
+ args: [pid] as const,
655
+ }));
656
+
657
+ const res = await multicallChunks({
658
+ client: params.client as any,
659
+ contracts: calls as any,
660
+ chunkSize,
661
+ });
662
+
663
+ const out: ReadOraclePriceData[] = [];
664
+ for (let i = 0; i < res.length; i++) {
665
+ const r: any = (res as any[])[i];
666
+ if (r.status !== "success") {
667
+ throw new Error(`Oracle getPriceData multicall failed: ${String(r.error ?? "unknown")}`);
668
+ }
669
+ out.push(
670
+ await readOraclePriceData({
671
+ oracle: {
672
+ read: {
673
+ getPriceData: async () => r.result,
674
+ },
675
+ } as any,
676
+ positionId: params.positionIds[i]!,
677
+ }),
678
+ );
679
+ }
680
+ return out;
681
+ }
682
+
371
683
  // wraps: VarlaOracle.getConfiguredPositions
372
684
  export async function readConfiguredPositionIds(params: {
373
685
  oracle: {
@@ -457,7 +769,7 @@ function normalizePositionSnapshot(raw: unknown, positionId: bigint): ReadPositi
457
769
  const priceE8 = r.priceE8 ?? r[1];
458
770
  const manuallyInvalidated = r.manuallyInvalidated ?? r[2];
459
771
  const riskTier = r.riskTier ?? r[3];
460
- const earlyClosureFactorWad = r.earlyClosureFactorWad ?? r[4];
772
+ const earlyClosureFactorWad = r.earlyClosureFactorWad ?? r.ltvAdjustmentFactorWad ?? r[4];
461
773
  const lastRecoveryFromStale = r.lastRecoveryFromStale_ ?? r.lastRecoveryFromStale ?? r[5];
462
774
  const isFinalized = r.isFinalized_ ?? r.isFinalized ?? r[6];
463
775
 
@@ -741,3 +1053,78 @@ export async function hydrateOraclePositionIds(params: {
741
1053
  ]);
742
1054
  return { positionIds: [...params.positionIds], snapshots, meta };
743
1055
  }
1056
+
1057
+ /**
1058
+ * Aggregate reader for the admin Oracle page.
1059
+ */
1060
+ // wraps: VarlaOracle.maxStaleness,VarlaOracle.liquidationGracePeriod,VarlaOracle.defaultEarlyClosureWindow,VarlaOracle.updater,VarlaOracle.collateralDecimals,VarlaOracle.getConfiguredPositions,VarlaOracle.getPositionSnapshot,VarlaOracle.getConditionId,VarlaOracle.getResolutionTime,VarlaOracle.getOppositePositionId,VarlaOracle.isNegRisk,VarlaOracle.getNegRiskMarketId,VarlaOracle.isManuallyInvalidated,VarlaOracle.isConfigured,VarlaOracle.isResolved,VarlaOracle.isDepositAllowed,VarlaOracle.isValidCollateral,VarlaOracle.isPriceStale,VarlaOracle.getEarlyClosureWindow,VarlaOracle.getEarlyClosureFactor,VarlaOracle.lastRecoveryFromStale,VarlaOracle.getLastUpdated,VarlaOracle.tryGetPrice,VarlaOracle.getPriceData
1061
+ export async function readOracleDashboard(params: {
1062
+ oracle: {
1063
+ address: Address;
1064
+ read: {
1065
+ maxStaleness: () => Promise<bigint>;
1066
+ liquidationGracePeriod: () => Promise<bigint>;
1067
+ defaultEarlyClosureWindow: () => Promise<bigint>;
1068
+ updater: () => Promise<Address>;
1069
+ collateralDecimals: () => Promise<number | bigint>;
1070
+ getConfiguredPositions: () => Promise<readonly bigint[]>;
1071
+ };
1072
+ };
1073
+ client: { multicall: (args: any) => Promise<any> };
1074
+ chunkSize?: number;
1075
+ }): Promise<ReadOracleDashboard> {
1076
+ const chunkSize = params.chunkSize ?? 256;
1077
+ const [config, basics, positionIds] = await Promise.all([
1078
+ readOracleConfig({ oracle: params.oracle }),
1079
+ readOracleBasics({ oracle: params.oracle }),
1080
+ readConfiguredPositionIds({ oracle: params.oracle }),
1081
+ ]);
1082
+
1083
+ if (positionIds.length === 0) {
1084
+ return { config, basics, positionIds: [], rows: [] };
1085
+ }
1086
+
1087
+ const [snapshots, meta, statusFlags, priceData] = await Promise.all([
1088
+ readManyPositionSnapshots({
1089
+ oracle: { address: params.oracle.address },
1090
+ client: params.client,
1091
+ positionIds,
1092
+ chunkSize,
1093
+ }),
1094
+ readManyOraclePositionMeta({
1095
+ oracle: { address: params.oracle.address },
1096
+ client: params.client,
1097
+ positionIds,
1098
+ chunkSize,
1099
+ }),
1100
+ readManyOracleStatusFlags({
1101
+ oracle: { address: params.oracle.address },
1102
+ client: params.client,
1103
+ positionIds,
1104
+ chunkSize,
1105
+ }),
1106
+ readManyOraclePriceData({
1107
+ oracle: { address: params.oracle.address },
1108
+ client: params.client,
1109
+ positionIds,
1110
+ chunkSize,
1111
+ }),
1112
+ ]);
1113
+
1114
+ const rows = positionIds.map((positionId, i) => ({
1115
+ positionId,
1116
+ snapshot: snapshots[i]!,
1117
+ meta: meta[i]!,
1118
+ resolvedStatus: {
1119
+ positionId,
1120
+ isResolved: statusFlags[i]!.isResolved,
1121
+ },
1122
+ staleStatus: {
1123
+ positionId,
1124
+ isPriceStale: statusFlags[i]!.isPriceStale,
1125
+ },
1126
+ priceData: priceData[i]!,
1127
+ }));
1128
+
1129
+ return { config, basics, positionIds, rows };
1130
+ }
package/src/views/pool.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  // Note: explicit .js extension is required for Node ESM resolution.
2
2
 
3
3
  import type { Address } from "viem";
4
+ import { multicallChunks } from "../batch.js";
5
+ import { abis } from "../generated.js";
4
6
 
5
7
  export type ReadPoolSnapshot = {
6
8
  totalAssets: bigint;
@@ -154,6 +156,16 @@ export type ReadPoolHealthScore =
154
156
  kind: "no-liquidity";
155
157
  };
156
158
 
159
+ export type ReadPoolDashboard = {
160
+ snapshot: ReadPoolSnapshot;
161
+ caps: ReadPoolCaps;
162
+ accounting: ReadPoolAccounting;
163
+ rates: ReadPoolRates;
164
+ sharePrice: ReadPoolSharePrice;
165
+ healthScore: ReadPoolHealthScore;
166
+ debtState: ReadPoolDebtState;
167
+ };
168
+
157
169
  /**
158
170
  * Reads pool “rates” and near-term capacity in one convenience call.
159
171
  */
@@ -275,3 +287,144 @@ export async function readPoolCoreAddress(params: {
275
287
  }): Promise<Address> {
276
288
  return params.pool.read.varlaCore();
277
289
  }
290
+
291
+ /**
292
+ * Multicall-backed aggregate reader for pool-heavy UIs.
293
+ *
294
+ * Deduplicates the overlapping primitives used across snapshot/caps/accounting/rates/share price
295
+ * so frontends can hydrate the full pool page with one batched request group.
296
+ */
297
+ // wraps: VarlaPool.getPoolStats,VarlaPool.depositCap,VarlaPool.borrowCap,VarlaPool.reserveBalance,VarlaPool.scaledTotalBorrowed,VarlaPool.lastUpdateTimestamp,VarlaPool.totalSupply,VarlaPool.maxBorrow,VarlaPool.getCurrentBorrowIndex
298
+ export async function readPoolDashboard(params: {
299
+ pool: { address: Address };
300
+ client: { multicall: (args: any) => Promise<any> };
301
+ chunkSize?: number;
302
+ }): Promise<ReadPoolDashboard> {
303
+ const chunkSize = params.chunkSize ?? 256;
304
+
305
+ const calls = [
306
+ {
307
+ address: params.pool.address,
308
+ abi: abis.VARLAPOOL_ABI,
309
+ functionName: "getPoolStats" as const,
310
+ },
311
+ {
312
+ address: params.pool.address,
313
+ abi: abis.VARLAPOOL_ABI,
314
+ functionName: "depositCap" as const,
315
+ },
316
+ {
317
+ address: params.pool.address,
318
+ abi: abis.VARLAPOOL_ABI,
319
+ functionName: "borrowCap" as const,
320
+ },
321
+ {
322
+ address: params.pool.address,
323
+ abi: abis.VARLAPOOL_ABI,
324
+ functionName: "reserveBalance" as const,
325
+ },
326
+ {
327
+ address: params.pool.address,
328
+ abi: abis.VARLAPOOL_ABI,
329
+ functionName: "scaledTotalBorrowed" as const,
330
+ },
331
+ {
332
+ address: params.pool.address,
333
+ abi: abis.VARLAPOOL_ABI,
334
+ functionName: "lastUpdateTimestamp" as const,
335
+ },
336
+ {
337
+ address: params.pool.address,
338
+ abi: abis.VARLAPOOL_ABI,
339
+ functionName: "totalSupply" as const,
340
+ },
341
+ {
342
+ address: params.pool.address,
343
+ abi: abis.VARLAPOOL_ABI,
344
+ functionName: "maxBorrow" as const,
345
+ },
346
+ {
347
+ address: params.pool.address,
348
+ abi: abis.VARLAPOOL_ABI,
349
+ functionName: "getCurrentBorrowIndex" as const,
350
+ },
351
+ ];
352
+
353
+ const res = await multicallChunks({
354
+ client: params.client as any,
355
+ contracts: calls as any,
356
+ chunkSize,
357
+ });
358
+
359
+ for (const r of res as any[]) {
360
+ if (r.status !== "success") {
361
+ throw new Error(`Pool dashboard multicall failed: ${String(r.error ?? "unknown")}`);
362
+ }
363
+ }
364
+
365
+ const snapshot = await readPoolSnapshot({
366
+ pool: {
367
+ read: {
368
+ getPoolStats: async () => (res as any[])[0]!.result,
369
+ },
370
+ },
371
+ });
372
+
373
+ const depositCap = (res as any[])[1]!.result as bigint;
374
+ const borrowCap = (res as any[])[2]!.result as bigint;
375
+ const reserveBalance = (res as any[])[3]!.result as bigint;
376
+ const scaledTotalBorrowed = (res as any[])[4]!.result as bigint;
377
+ const lastUpdateTimestamp = BigInt((res as any[])[5]!.result);
378
+ const totalShares = (res as any[])[6]!.result as bigint;
379
+ const maxBorrow = (res as any[])[7]!.result as bigint;
380
+ const currentBorrowIndex = (res as any[])[8]!.result as bigint;
381
+
382
+ const sharePrice: ReadPoolSharePrice = {
383
+ totalAssets: snapshot.totalAssets,
384
+ totalShares,
385
+ assetsPerShareWad:
386
+ totalShares === 0n ? 0n : (snapshot.totalAssets * 1_000_000_000_000_000_000n) / totalShares,
387
+ };
388
+
389
+ const healthScore: ReadPoolHealthScore =
390
+ snapshot.totalAssets === 0n
391
+ ? { kind: "no-liquidity" }
392
+ : {
393
+ kind: "ok",
394
+ utilizationWad: snapshot.utilization,
395
+ scoreBps:
396
+ ((1_000_000_000_000_000_000n -
397
+ (snapshot.utilization > 1_000_000_000_000_000_000n
398
+ ? 1_000_000_000_000_000_000n
399
+ : snapshot.utilization < 0n
400
+ ? 0n
401
+ : snapshot.utilization)) *
402
+ 10_000n) /
403
+ 1_000_000_000_000_000_000n,
404
+ };
405
+
406
+ return {
407
+ snapshot,
408
+ caps: { depositCap, borrowCap },
409
+ accounting: {
410
+ reserveBalance,
411
+ scaledTotalBorrowed,
412
+ borrowIndex: snapshot.borrowIndex,
413
+ lastUpdateTimestamp,
414
+ },
415
+ rates: {
416
+ utilization: snapshot.utilization,
417
+ borrowRate: snapshot.borrowRate,
418
+ supplyAPY: snapshot.supplyAPY,
419
+ availableLiquidity: snapshot.available,
420
+ maxBorrow,
421
+ },
422
+ sharePrice,
423
+ healthScore,
424
+ debtState: {
425
+ totalBorrowed: snapshot.totalBorrowed,
426
+ currentBorrowIndex,
427
+ borrowRate: snapshot.borrowRate,
428
+ },
429
+ };
430
+ }
@@ -1,11 +1,17 @@
1
1
  // Note: explicit .js extension is required for Node ESM resolution.
2
2
 
3
3
  import type { ReadDefaultLtvConfig, ReadLiquidationConfig } from "./core.js";
4
- import { readDefaultLtvConfig, readLiquidationConfig } from "./core.js";
4
+ import { readCoreDashboard, readDefaultLtvConfig, readLiquidationConfig } from "./core.js";
5
5
  import type { ReadOracleConfig } from "./oracle.js";
6
6
  import { readOracleConfig } from "./oracle.js";
7
- import type { ReadPoolAccounting, ReadPoolCaps, ReadPoolSnapshot } from "./pool.js";
8
- import { readPoolAccounting, readPoolCaps, readPoolSnapshot } from "./pool.js";
7
+ import type {
8
+ ReadPoolAccounting,
9
+ ReadPoolCaps,
10
+ ReadPoolHealthScore,
11
+ ReadPoolSharePrice,
12
+ ReadPoolSnapshot,
13
+ } from "./pool.js";
14
+ import { readPoolAccounting, readPoolCaps, readPoolDashboard, readPoolSnapshot } from "./pool.js";
9
15
 
10
16
  export type ReadSystemSnapshot = {
11
17
  pool: ReadPoolSnapshot;
@@ -16,6 +22,11 @@ export type ReadSystemSnapshot = {
16
22
  oracle: ReadOracleConfig;
17
23
  };
18
24
 
25
+ export type ReadSystemDashboardSnapshot = ReadSystemSnapshot & {
26
+ poolSharePrice: ReadPoolSharePrice;
27
+ poolHealthScore: ReadPoolHealthScore;
28
+ };
29
+
19
30
  /**
20
31
  * A lightweight “global protocol state” read for dashboards.
21
32
  *
@@ -40,3 +51,40 @@ export async function readSystemSnapshot(params: {
40
51
 
41
52
  return { pool, poolCaps, poolAccounting, coreLtv, coreLiquidation, oracle };
42
53
  }
54
+
55
+ /**
56
+ * Multicall-backed system snapshot used by dashboards.
57
+ */
58
+ export async function readSystemDashboardSnapshot(params: {
59
+ pool: { address: `0x${string}` };
60
+ core: { address: `0x${string}` };
61
+ oracle: any;
62
+ client: { multicall: (args: any) => Promise<any> };
63
+ }): Promise<ReadSystemDashboardSnapshot> {
64
+ const [poolDashboard, coreDashboard, oracle] = await Promise.all([
65
+ readPoolDashboard({
66
+ pool: { address: params.pool.address },
67
+ client: params.client,
68
+ }),
69
+ readCoreDashboard({
70
+ core: { address: params.core.address },
71
+ client: params.client,
72
+ }),
73
+ readOracleConfig({ oracle: params.oracle }),
74
+ ]);
75
+
76
+ return {
77
+ pool: poolDashboard.snapshot,
78
+ poolCaps: poolDashboard.caps,
79
+ poolAccounting: poolDashboard.accounting,
80
+ coreLtv: {
81
+ conservative: coreDashboard.ltvConfig.tier0,
82
+ moderate: coreDashboard.ltvConfig.tier1,
83
+ risk: coreDashboard.ltvConfig.tier2,
84
+ },
85
+ coreLiquidation: coreDashboard.liqConfig,
86
+ oracle,
87
+ poolSharePrice: poolDashboard.sharePrice,
88
+ poolHealthScore: poolDashboard.healthScore,
89
+ };
90
+ }