@kasufinance/kasu-sdk 2.15.0 → 2.16.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.
@@ -875,6 +875,52 @@ export class UserLending {
875
875
  );
876
876
  }
877
877
 
878
+ /**
879
+ * The lender's ACTIVE assets in each tranche, read from the chain
880
+ * (`LendingPoolTranche.userActiveAssets`), in the order of `trancheIds`,
881
+ * in the stable token's base units. Active shares are the ones the lender
882
+ * owns in the tranche — fixed-term-locked and withdrawal-pending shares
883
+ * included — whatever path brought them there; `LendingPool.userBalance`
884
+ * is their sum. One call per tranche.
885
+ */
886
+ async getUserTrancheActiveAssets(
887
+ user: string,
888
+ trancheIds: readonly string[],
889
+ provider?: Provider,
890
+ blockTag?: number,
891
+ ): Promise<BigNumber[]> {
892
+ return await Promise.all(
893
+ trancheIds.map((trancheId) =>
894
+ ILendingPoolTrancheAbi__factory.connect(
895
+ trancheId,
896
+ provider ?? this._signerOrProvider,
897
+ ).userActiveAssets(user, { blockTag: blockTag ?? 'latest' }),
898
+ ),
899
+ );
900
+ }
901
+
902
+ /**
903
+ * The lender's balance in each pool (`LendingPool.userBalance`, the sum
904
+ * of its tranches' active assets), in the order of `poolIds`, in the
905
+ * stable token's base units, at `blockTag` (default `latest`). One call
906
+ * per pool.
907
+ */
908
+ async getUserPoolBalancesAt(
909
+ user: string,
910
+ poolIds: readonly string[],
911
+ provider?: Provider,
912
+ blockTag?: number,
913
+ ): Promise<BigNumber[]> {
914
+ return await Promise.all(
915
+ poolIds.map((poolId) =>
916
+ ILendingPoolAbi__factory.connect(
917
+ poolId,
918
+ provider ?? this._signerOrProvider,
919
+ ).userBalance(user, { blockTag: blockTag ?? 'latest' }),
920
+ ),
921
+ );
922
+ }
923
+
878
924
  async getPortfolioUserTrancheBalances(
879
925
  user: string,
880
926
  ): Promise<PortfolioUserTrancheBalance> {
@@ -46,3 +46,51 @@ export function isEmptyHistoryError(
46
46
  ): boolean {
47
47
  return liteDeployment && isMissingEntityError(error);
48
48
  }
49
+
50
+ /**
51
+ * What node implementations and RPC providers answer when they cannot serve
52
+ * state at a requested block: pruned history, or a block they have not seen
53
+ * yet. Matched on the error's text, lowercased.
54
+ */
55
+ const BLOCK_UNAVAILABLE_PATTERNS = [
56
+ 'missing trie node',
57
+ 'header not found',
58
+ 'unknown block',
59
+ 'block not found',
60
+ 'pruned',
61
+ 'historical state',
62
+ 'state is not available',
63
+ 'state not available',
64
+ 'missing state',
65
+ 'state unavailable',
66
+ 'state histories',
67
+ 'block number out of range',
68
+ 'beyond current head',
69
+ 'is in the future',
70
+ ] as const;
71
+
72
+ function errorTexts(error: unknown, depth = 0): string[] {
73
+ if (depth > 4 || error === null || error === undefined) return [];
74
+ if (typeof error === 'string') return [error];
75
+ if (typeof error !== 'object') return [];
76
+ const record = error as Record<string, unknown>;
77
+ return [
78
+ 'message',
79
+ 'reason',
80
+ 'body',
81
+ 'error',
82
+ 'data',
83
+ 'serverError',
84
+ ].flatMap((key) => errorTexts(record[key], depth + 1));
85
+ }
86
+
87
+ /**
88
+ * True when a failed RPC read says the node cannot serve state at the block
89
+ * asked for (pruned, or not reached yet) — and ONLY then. A rate limit, a
90
+ * timeout, a refused connection or a revert is not this, and must surface as
91
+ * the error it is.
92
+ */
93
+ export function isBlockUnavailableError(error: unknown): boolean {
94
+ const text = errorTexts(error).join(' ').toLowerCase();
95
+ return BLOCK_UNAVAILABLE_PATTERNS.some((pattern) => text.includes(pattern));
96
+ }