@varla/sdk 2.17.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.
@@ -2,6 +2,8 @@
2
2
 
3
3
  import type { Address, PublicClient } from "viem";
4
4
 
5
+ import { multicallChunks } from "../batch.js";
6
+ import { abis } from "../generated.js";
5
7
  import { readAccountPositionsFull } from "./core.js";
6
8
  import { type ReadPositionSnapshot, readManyPositionSnapshots } from "./oracle.js";
7
9
 
@@ -51,17 +53,8 @@ export type ReadCanLiquidate = {
51
53
  currentBonus: bigint;
52
54
  };
53
55
 
54
- /**
55
- * Normalized wrapper over `*.canLiquidate(user)`.
56
- */
57
- // wraps: VarlaLiquidator.canLiquidate,VarlaMergeLiquidator.canLiquidate,VarlaConvertLiquidator.canLiquidate
58
- export async function readCanLiquidate(params: {
59
- liquidator: CanLiquidateLike;
60
- user: Address;
61
- }): Promise<ReadCanLiquidate> {
62
- const raw = await params.liquidator.read.canLiquidate([params.user]);
56
+ function normalizeCanLiquidate(raw: unknown, user: Address): ReadCanLiquidate {
63
57
  const r: any = raw as any;
64
-
65
58
  const canLiquidate = r.canLiquidate_ ?? r.canLiquidate ?? r[0];
66
59
  const healthFactor = r.healthFactor ?? r[1];
67
60
  const currentBonus = r.currentBonus ?? r[2];
@@ -74,7 +67,56 @@ export async function readCanLiquidate(params: {
74
67
  throw new Error("Unexpected canLiquidate() return shape");
75
68
  }
76
69
 
77
- return { user: params.user, canLiquidate, healthFactor, currentBonus };
70
+ return { user, canLiquidate, healthFactor, currentBonus };
71
+ }
72
+
73
+ /**
74
+ * Normalized wrapper over `*.canLiquidate(user)`.
75
+ */
76
+ // wraps: VarlaLiquidator.canLiquidate,VarlaMergeLiquidator.canLiquidate,VarlaConvertLiquidator.canLiquidate
77
+ export async function readCanLiquidate(params: {
78
+ liquidator: CanLiquidateLike;
79
+ user: Address;
80
+ }): Promise<ReadCanLiquidate> {
81
+ const raw = await params.liquidator.read.canLiquidate([params.user]);
82
+ return normalizeCanLiquidate(raw, params.user);
83
+ }
84
+
85
+ /**
86
+ * Multicall-chunked version of `readCanLiquidate`.
87
+ */
88
+ // wraps: VarlaLiquidator.canLiquidate,VarlaMergeLiquidator.canLiquidate,VarlaConvertLiquidator.canLiquidate
89
+ export async function readManyCanLiquidate(params: {
90
+ liquidator: { address: Address };
91
+ client: { multicall: (args: any) => Promise<any> };
92
+ users: readonly Address[];
93
+ chunkSize?: number;
94
+ }): Promise<ReadCanLiquidate[]> {
95
+ const chunkSize = params.chunkSize ?? 256;
96
+ if (params.users.length === 0) return [];
97
+
98
+ const calls = params.users.map((user) => ({
99
+ address: params.liquidator.address,
100
+ abi: abis.VARLALIQUIDATOR_ABI,
101
+ functionName: "canLiquidate" as const,
102
+ args: [user] as const,
103
+ }));
104
+
105
+ const res = await multicallChunks({
106
+ client: params.client as any,
107
+ contracts: calls as any,
108
+ chunkSize,
109
+ });
110
+
111
+ const out: ReadCanLiquidate[] = [];
112
+ for (let i = 0; i < res.length; i++) {
113
+ const r: any = (res as any[])[i];
114
+ if (r.status !== "success") {
115
+ throw new Error(`Liquidator canLiquidate multicall failed: ${String(r.error ?? "unknown")}`);
116
+ }
117
+ out.push(normalizeCanLiquidate(r.result, params.users[i]!));
118
+ }
119
+ return out;
78
120
  }
79
121
 
80
122
  type PreviewLiquidationLike = {
@@ -171,15 +213,7 @@ export type ReadBadDebtStatus = {
171
213
  shortfall: bigint;
172
214
  };
173
215
 
174
- /**
175
- * Normalized wrapper over `VarlaLiquidator.checkBadDebt(user)`.
176
- */
177
- // wraps: VarlaLiquidator.checkBadDebt
178
- export async function readBadDebtStatus(params: {
179
- liquidator: CheckBadDebtLike;
180
- user: Address;
181
- }): Promise<ReadBadDebtStatus> {
182
- const raw = await params.liquidator.read.checkBadDebt([params.user]);
216
+ function normalizeBadDebtStatus(raw: unknown, user: Address): ReadBadDebtStatus {
183
217
  const r: any = raw as any;
184
218
 
185
219
  const hasBadDebt = r.hasBadDebt ?? r[0];
@@ -196,7 +230,56 @@ export async function readBadDebtStatus(params: {
196
230
  throw new Error("Unexpected checkBadDebt() return shape");
197
231
  }
198
232
 
199
- return { user: params.user, hasBadDebt, collateralValue, debtValue, shortfall };
233
+ return { user, hasBadDebt, collateralValue, debtValue, shortfall };
234
+ }
235
+
236
+ /**
237
+ * Normalized wrapper over `VarlaLiquidator.checkBadDebt(user)`.
238
+ */
239
+ // wraps: VarlaLiquidator.checkBadDebt
240
+ export async function readBadDebtStatus(params: {
241
+ liquidator: CheckBadDebtLike;
242
+ user: Address;
243
+ }): Promise<ReadBadDebtStatus> {
244
+ const raw = await params.liquidator.read.checkBadDebt([params.user]);
245
+ return normalizeBadDebtStatus(raw, params.user);
246
+ }
247
+
248
+ /**
249
+ * Multicall-chunked version of `readBadDebtStatus`.
250
+ */
251
+ // wraps: VarlaLiquidator.checkBadDebt
252
+ export async function readManyBadDebtStatus(params: {
253
+ liquidator: { address: Address };
254
+ client: { multicall: (args: any) => Promise<any> };
255
+ users: readonly Address[];
256
+ chunkSize?: number;
257
+ }): Promise<ReadBadDebtStatus[]> {
258
+ const chunkSize = params.chunkSize ?? 256;
259
+ if (params.users.length === 0) return [];
260
+
261
+ const calls = params.users.map((user) => ({
262
+ address: params.liquidator.address,
263
+ abi: abis.VARLALIQUIDATOR_ABI,
264
+ functionName: "checkBadDebt" as const,
265
+ args: [user] as const,
266
+ }));
267
+
268
+ const res = await multicallChunks({
269
+ client: params.client as any,
270
+ contracts: calls as any,
271
+ chunkSize,
272
+ });
273
+
274
+ const out: ReadBadDebtStatus[] = [];
275
+ for (let i = 0; i < res.length; i++) {
276
+ const r: any = (res as any[])[i];
277
+ if (r.status !== "success") {
278
+ throw new Error(`Liquidator checkBadDebt multicall failed: ${String(r.error ?? "unknown")}`);
279
+ }
280
+ out.push(normalizeBadDebtStatus(r.result, params.users[i]!));
281
+ }
282
+ return out;
200
283
  }
201
284
 
202
285
  type MergeLiquidatorLike = LiquidatorLike & {
@@ -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
+ }