@kasufinance/kasu-sdk 1.0.4 → 2.2.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.
Files changed (84) hide show
  1. package/CLAUDE.md +410 -0
  2. package/README.md +179 -12
  3. package/dist/bundle.cjs.js +658 -90
  4. package/dist/bundle.esm.js +654 -92
  5. package/dist/facade/chain-configs.d.ts +11 -0
  6. package/dist/facade/chain-configs.js +84 -0
  7. package/dist/facade/chain-configs.js.map +1 -0
  8. package/dist/facade/deposits.d.ts +56 -0
  9. package/dist/facade/deposits.js +87 -0
  10. package/dist/facade/deposits.js.map +1 -0
  11. package/dist/facade/index.d.ts +6 -0
  12. package/dist/facade/index.js +9 -0
  13. package/dist/facade/index.js.map +1 -0
  14. package/dist/facade/kasu.d.ts +71 -0
  15. package/dist/facade/kasu.js +101 -0
  16. package/dist/facade/kasu.js.map +1 -0
  17. package/dist/facade/strategies.d.ts +37 -0
  18. package/dist/facade/strategies.js +112 -0
  19. package/dist/facade/strategies.js.map +1 -0
  20. package/dist/facade/types.d.ts +127 -0
  21. package/dist/facade/types.js +2 -0
  22. package/dist/facade/types.js.map +1 -0
  23. package/dist/facade/user-portfolio.d.ts +35 -0
  24. package/dist/facade/user-portfolio.js +53 -0
  25. package/dist/facade/user-portfolio.js.map +1 -0
  26. package/dist/index.d.ts +9 -17
  27. package/dist/index.js +9 -16
  28. package/dist/index.js.map +1 -1
  29. package/dist/kasu-sdk.d.ts +17 -0
  30. package/dist/kasu-sdk.js +17 -0
  31. package/dist/kasu-sdk.js.map +1 -0
  32. package/dist/sdk-config.d.ts +34 -4
  33. package/dist/sdk-config.js +9 -6
  34. package/dist/sdk-config.js.map +1 -1
  35. package/dist/services/DataService/data-service.d.ts +9 -2
  36. package/dist/services/DataService/data-service.js +91 -53
  37. package/dist/services/DataService/data-service.js.map +1 -1
  38. package/dist/services/DataService/queries.d.ts +0 -1
  39. package/dist/services/DataService/queries.js +0 -8
  40. package/dist/services/DataService/queries.js.map +1 -1
  41. package/dist/services/DataService/subgraph-types.d.ts +0 -6
  42. package/dist/services/DataService/types.d.ts +0 -1
  43. package/dist/services/Locking/locking.d.ts +9 -3
  44. package/dist/services/Locking/locking.js +114 -20
  45. package/dist/services/Locking/locking.js.map +1 -1
  46. package/dist/services/Locking/types.d.ts +1 -1
  47. package/dist/services/Portfolio/portfolio.d.ts +3 -2
  48. package/dist/services/Portfolio/portfolio.js +15 -7
  49. package/dist/services/Portfolio/portfolio.js.map +1 -1
  50. package/dist/services/Portfolio/types.d.ts +2 -2
  51. package/dist/services/UserLending/user-lending.d.ts +3 -1
  52. package/dist/services/UserLending/user-lending.js +9 -3
  53. package/dist/services/UserLending/user-lending.js.map +1 -1
  54. package/dist/tests/facade.test.d.ts +1 -0
  55. package/dist/tests/facade.test.js +196 -0
  56. package/dist/tests/facade.test.js.map +1 -0
  57. package/dist/tests/sample.test.js +19 -14
  58. package/dist/tests/sample.test.js.map +1 -1
  59. package/dist/utils/deployment-mode.d.ts +6 -0
  60. package/dist/utils/deployment-mode.js +8 -0
  61. package/dist/utils/deployment-mode.js.map +1 -0
  62. package/package.json +2 -2
  63. package/src/facade/chain-configs.ts +93 -0
  64. package/src/facade/deposits.ts +102 -0
  65. package/src/facade/index.ts +26 -0
  66. package/src/facade/kasu.ts +144 -0
  67. package/src/facade/strategies.ts +119 -0
  68. package/src/facade/types.ts +168 -0
  69. package/src/facade/user-portfolio.ts +73 -0
  70. package/src/index.ts +59 -25
  71. package/src/kasu-sdk.ts +27 -0
  72. package/src/sdk-config.ts +43 -15
  73. package/src/services/DataService/data-service.ts +106 -66
  74. package/src/services/DataService/queries.ts +0 -9
  75. package/src/services/DataService/subgraph-types.ts +0 -7
  76. package/src/services/DataService/types.ts +0 -1
  77. package/src/services/Locking/locking.ts +141 -25
  78. package/src/services/Locking/types.ts +1 -1
  79. package/src/services/Portfolio/portfolio.ts +21 -11
  80. package/src/services/Portfolio/types.ts +2 -2
  81. package/src/services/UserLending/user-lending.ts +13 -4
  82. package/src/tests/facade.test.ts +245 -0
  83. package/src/tests/sample.test.ts +7 -9
  84. package/src/utils/deployment-mode.ts +9 -0
package/src/sdk-config.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export interface ContractAddresses {
2
- KSUToken: string;
2
+ /** KSU Token address - only present on Full deployments (Base), undefined on Lite (XDC, Plume) */
3
+ KSUToken?: string;
3
4
  IKSULocking: string;
4
5
  IKSULockBonus: string;
5
6
  UserManager: string;
@@ -9,27 +10,54 @@ export interface ContractAddresses {
9
10
  UserLoyaltyRewards: string;
10
11
  KsuPrice: string;
11
12
  ClearingCoordinator: string;
12
- KasuNFTs: string;
13
+ /** KasuNFTs address - only present on Full deployments */
14
+ KasuNFTs?: string;
13
15
  ExternalTVL: string;
14
16
  }
17
+
18
+ export interface SdkConfigOptions {
19
+ subgraphUrl: string;
20
+ contracts: ContractAddresses;
21
+ /** Directus CMS URL. Optional – when omitted, pool descriptions/images will not be available but on-chain data still works. */
22
+ directusUrl?: string;
23
+ UNUSED_LENDING_POOL_IDS: string[];
24
+ /**
25
+ * Whether this is a Lite deployment (no KSU token, locking, or loyalty features).
26
+ * Set to true for XDC, Plume, and other chains without the full token system.
27
+ * @default false
28
+ */
29
+ isLiteDeployment?: boolean;
30
+ /**
31
+ * Maps pool addresses to their metadata source pool address in Directus.
32
+ * Used for chains (XDC, Plume) that share pool descriptions with Base.
33
+ * Key: pool address on this chain (lowercase)
34
+ * Value: pool address in Directus / Base pool (lowercase)
35
+ */
36
+ poolMetadataMapping?: Record<string, string>;
37
+ /**
38
+ * Decimals of the stable asset used by lending pools (e.g. 6 for USDC/AUDD).
39
+ * @default 6
40
+ */
41
+ stableAssetDecimals?: number;
42
+ }
43
+
15
44
  export class SdkConfig {
16
45
  subgraphUrl: string;
17
- plumeSubgraphUrl: string;
18
46
  contracts: ContractAddresses;
47
+ /** Directus CMS URL. When empty, DataService skips CMS enrichment and returns on-chain data only. */
19
48
  directusUrl: string;
20
49
  UNUSED_LENDING_POOL_IDS: string[];
50
+ isLiteDeployment: boolean;
51
+ poolMetadataMapping: Record<string, string>;
52
+ stableAssetDecimals: number;
21
53
 
22
- constructor(
23
- subgraphUrl: string,
24
- plumeSubgraphUrl: string,
25
- contracts: ContractAddresses,
26
- directusUrl: string,
27
- UNUSED_LENDING_POOL_IDS: string[],
28
- ) {
29
- this.subgraphUrl = subgraphUrl;
30
- this.plumeSubgraphUrl = plumeSubgraphUrl;
31
- this.contracts = contracts;
32
- this.directusUrl = directusUrl;
33
- this.UNUSED_LENDING_POOL_IDS = UNUSED_LENDING_POOL_IDS;
54
+ constructor(options: SdkConfigOptions) {
55
+ this.subgraphUrl = options.subgraphUrl;
56
+ this.contracts = options.contracts;
57
+ this.directusUrl = options.directusUrl ?? '';
58
+ this.UNUSED_LENDING_POOL_IDS = options.UNUSED_LENDING_POOL_IDS;
59
+ this.isLiteDeployment = options.isLiteDeployment ?? false;
60
+ this.poolMetadataMapping = options.poolMetadataMapping ?? {};
61
+ this.stableAssetDecimals = options.stableAssetDecimals ?? 6;
34
62
  }
35
63
  }
@@ -38,7 +38,6 @@ import {
38
38
  getAllTrancheConfigurationsQuery,
39
39
  getAllTranchesQuery,
40
40
  getLendingPoolWithdrawalAndDepositsQuery,
41
- getPlumeTvl,
42
41
  getPoolNameQuery,
43
42
  getPoolOverviewQuery,
44
43
  } from './queries';
@@ -47,7 +46,6 @@ import {
47
46
  LendingPoolSubgraph,
48
47
  LendingPoolSubgraphReturn,
49
48
  LendingPoolWithdrawalAndDepositSubgraph,
50
- PlumeSubgraphReturn,
51
49
  TrancheConfigurationSubgraphResult,
52
50
  TrancheSubgraphResult,
53
51
  } from './subgraph-types';
@@ -67,13 +65,11 @@ import {
67
65
 
68
66
  export class DataService {
69
67
  private readonly _graph: GraphQLClient;
70
- private readonly _plumeGraph: GraphQLClient;
71
68
  private readonly _externalTvlAbi: KasuPoolExternalTVLAbi;
72
69
  private readonly _directus: DirectusClient<DirectusSchema> &
73
70
  AuthenticationClient<DirectusSchema> &
74
71
  RestClient<DirectusSchema>;
75
72
  private _directusPoolOverview: PoolOverviewDirectus[] | undefined;
76
- private _plumeTvl = new Map<string, string>();
77
73
 
78
74
  constructor(
79
75
  private _kasuConfig: SdkConfig,
@@ -84,17 +80,39 @@ export class DataService {
84
80
  signerOrProvider,
85
81
  );
86
82
  this._graph = new GraphQLClient(_kasuConfig.subgraphUrl);
87
- this._plumeGraph = new GraphQLClient(_kasuConfig.plumeSubgraphUrl);
88
83
 
89
- this._directus = createDirectus<DirectusSchema>(_kasuConfig.directusUrl)
90
- .with(authentication())
91
- .with(rest());
84
+ if (_kasuConfig.directusUrl) {
85
+ this._directus = createDirectus<DirectusSchema>(
86
+ _kasuConfig.directusUrl,
87
+ )
88
+ .with(authentication())
89
+ .with(rest());
90
+ } else {
91
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
92
+ this._directus = null as any;
93
+ }
92
94
  }
93
95
 
94
96
  private getUrlFromFile(fileName: string): string {
95
97
  return `${this._kasuConfig.directusUrl}assets/${fileName}`;
96
98
  }
97
99
 
100
+ /**
101
+ * Maps a pool ID to its metadata source pool ID.
102
+ * Used for chains (XDC, Plume) that share Directus metadata with Base pools.
103
+ */
104
+ private getMetadataPoolId(poolId: string): string {
105
+ const normalizedId = poolId.toLowerCase();
106
+ return this._kasuConfig.poolMetadataMapping[normalizedId] ?? normalizedId;
107
+ }
108
+
109
+ /**
110
+ * Maps an array of pool IDs to their metadata source pool IDs.
111
+ */
112
+ private getMetadataPoolIds(poolIds: string[]): string[] {
113
+ return poolIds.map((id) => this.getMetadataPoolId(id));
114
+ }
115
+
98
116
  calculatePoolCapacity(
99
117
  poolCapacities: number[],
100
118
  poolCapacityPercentages: number[],
@@ -181,7 +199,7 @@ export class DataService {
181
199
  );
182
200
 
183
201
  for (const [id, tvl] of results) {
184
- tvlMap.set(id, formatUnits(tvl, 6));
202
+ tvlMap.set(id, formatUnits(tvl, this._kasuConfig.stableAssetDecimals));
185
203
  }
186
204
  } catch (error) {
187
205
  console.error('getOffChainTvl :', error);
@@ -206,25 +224,28 @@ export class DataService {
206
224
  epochId,
207
225
  });
208
226
 
209
- if (!this._plumeTvl.size) {
210
- const plumeResults: PlumeSubgraphReturn =
211
- await this._plumeGraph.request(getPlumeTvl);
212
-
213
- for (const pool of plumeResults.lendingPools) {
214
- this._plumeTvl.set(pool.id, pool.balance);
215
- }
216
- }
217
-
218
227
  if (!this._directusPoolOverview) {
219
- this._directusPoolOverview = await this._directus.request(
220
- readItems('PoolOverview', {
221
- filter: {
222
- enabled: {
223
- _eq: true,
224
- },
225
- },
226
- }),
227
- );
228
+ if (this._kasuConfig.directusUrl) {
229
+ try {
230
+ this._directusPoolOverview = await this._directus.request(
231
+ readItems('PoolOverview', {
232
+ filter: {
233
+ enabled: {
234
+ _eq: true,
235
+ },
236
+ },
237
+ }),
238
+ );
239
+ } catch (error) {
240
+ console.warn(
241
+ 'Directus fetch failed, falling back to on-chain data only:',
242
+ error,
243
+ );
244
+ this._directusPoolOverview = [];
245
+ }
246
+ } else {
247
+ this._directusPoolOverview = [];
248
+ }
228
249
  }
229
250
 
230
251
  const offchainTvlMap = await this.getOffChainTvl(
@@ -233,9 +254,14 @@ export class DataService {
233
254
 
234
255
  const retn: PoolOverview[] = [];
235
256
  for (const lendingPoolSubgraph of poolOverviewResults.lendingPools) {
257
+ // Use metadata mapping if available (for XDC/Plume pools that share metadata with Base)
258
+ const metadataPoolId =
259
+ this._kasuConfig.poolMetadataMapping[lendingPoolSubgraph.id] ??
260
+ lendingPoolSubgraph.id;
261
+
236
262
  const lendingPoolDirectus: PoolOverviewDirectus | undefined =
237
263
  this._directusPoolOverview.find(
238
- (r) => r.id.toLowerCase() == lendingPoolSubgraph.id,
264
+ (r) => r.id.toLowerCase() === metadataPoolId,
239
265
  );
240
266
 
241
267
  const lendingPoolConfig = lendingPoolSubgraph.configuration;
@@ -244,8 +270,12 @@ export class DataService {
244
270
  console.warn(
245
271
  "Couldn't find directus pool for id: ",
246
272
  lendingPoolSubgraph.id,
273
+ metadataPoolId !== lendingPoolSubgraph.id
274
+ ? `(mapped from ${metadataPoolId})`
275
+ : '',
247
276
  );
248
- continue;
277
+ // When Directus is unavailable or pool has no CMS entry,
278
+ // use a fallback so the pool is still returned with on-chain data.
249
279
  }
250
280
  const poolCapacities = this.calculatePoolCapacity(
251
281
  [],
@@ -380,57 +410,56 @@ export class DataService {
380
410
  averageApy += parseFloat(trancheData.averageApy) * weight;
381
411
  }
382
412
 
383
- const plumeTvl =
384
- lendingPoolDirectus.plumeStrategy &&
385
- this._plumeTvl.has(lendingPoolDirectus.plumeStrategy)
386
- ? this._plumeTvl.get(lendingPoolDirectus.plumeStrategy) ??
387
- '0'
388
- : '0';
389
-
390
413
  const offChainTvl =
391
414
  offchainTvlMap.get(lendingPoolSubgraph.id) ?? '0';
392
415
 
393
416
  const poolOverview: PoolOverview = {
394
417
  id: lendingPoolSubgraph.id,
395
- security: lendingPoolDirectus.security,
396
- enabled: lendingPoolDirectus.enabled,
397
- isOversubscribed: lendingPoolDirectus.oversubscribed,
418
+ security: lendingPoolDirectus?.security ?? [],
419
+ enabled: lendingPoolDirectus?.enabled ?? true,
420
+ isOversubscribed:
421
+ lendingPoolDirectus?.oversubscribed ?? false,
398
422
  apy: averageApy,
399
- description: lendingPoolDirectus.description,
400
- liteDescription: lendingPoolDirectus.liteDescription,
401
- thumbnailImageUrl: this.getUrlFromFile(
402
- lendingPoolDirectus.thumbnailImage,
403
- ),
404
- bannerImageUrl: this.getUrlFromFile(
405
- lendingPoolDirectus.bannerImage,
406
- ),
407
- strategyDeckUrl: this.getUrlFromFile(
408
- lendingPoolDirectus.strategyDeck,
409
- ),
410
- assetClass: lendingPoolDirectus.assetClass,
411
- industryExposure: lendingPoolDirectus.industryExposure,
412
- poolApyStructure: lendingPoolDirectus.poolApyStructure,
413
- apyExpiryDate: lendingPoolDirectus.apyExpiryDate,
414
- poolInvestmentTerm: lendingPoolDirectus.poolInvestmentTerm,
415
- loanStructure: lendingPoolDirectus.loanStructure,
423
+ description: lendingPoolDirectus?.description ?? '',
424
+ liteDescription: lendingPoolDirectus?.liteDescription ?? '',
425
+ thumbnailImageUrl: lendingPoolDirectus?.thumbnailImage
426
+ ? this.getUrlFromFile(lendingPoolDirectus.thumbnailImage)
427
+ : '',
428
+ bannerImageUrl: lendingPoolDirectus?.bannerImage
429
+ ? this.getUrlFromFile(lendingPoolDirectus.bannerImage)
430
+ : '',
431
+ strategyDeckUrl: lendingPoolDirectus?.strategyDeck
432
+ ? this.getUrlFromFile(lendingPoolDirectus.strategyDeck)
433
+ : '',
434
+ assetClass: lendingPoolDirectus?.assetClass ?? '',
435
+ industryExposure:
436
+ lendingPoolDirectus?.industryExposure ?? '',
437
+ poolApyStructure:
438
+ lendingPoolDirectus?.poolApyStructure ?? 'Variable',
439
+ apyExpiryDate:
440
+ lendingPoolDirectus?.apyExpiryDate ?? null,
441
+ poolInvestmentTerm:
442
+ lendingPoolDirectus?.poolInvestmentTerm ?? '',
443
+ loanStructure: lendingPoolDirectus?.loanStructure ?? '',
416
444
  poolName:
417
- lendingPoolDirectus.poolName ?? lendingPoolSubgraph.name,
418
- subheading: lendingPoolDirectus.subheading,
445
+ lendingPoolDirectus?.poolName ??
446
+ lendingPoolSubgraph.name,
447
+ subheading: lendingPoolDirectus?.subheading,
419
448
  totalValueLocked: {
420
449
  total: (
421
450
  parseFloat(lendingPoolSubgraph.balance) +
422
- parseFloat(plumeTvl) +
423
451
  parseFloat(offChainTvl)
424
452
  ).toString(),
425
453
  offchain: offChainTvl,
426
- plume: plumeTvl,
427
454
  },
428
- loansUnderManagement: lendingPoolDirectus.loansUnderManagement,
455
+ loansUnderManagement:
456
+ lendingPoolDirectus?.loansUnderManagement ?? '0',
429
457
  yieldEarned: lendingPoolSubgraph.totalUserInterestAmount,
430
458
  poolCapacity: poolCapacitySum.toString(),
431
459
  poolCapacityPercentage: poolCapacityPercentageSum.toString(),
432
- activeLoans: lendingPoolDirectus.activeLoans,
433
- loanFundsOriginated: lendingPoolDirectus.loanFundsOriginated,
460
+ activeLoans: lendingPoolDirectus?.activeLoans ?? '0',
461
+ loanFundsOriginated:
462
+ lendingPoolDirectus?.loanFundsOriginated ?? '0',
434
463
  tranches: tranches,
435
464
  isActive: !lendingPoolSubgraph.isStopped,
436
465
  requestEpochsInAdvance:
@@ -438,7 +467,8 @@ export class DataService {
438
467
  .lendingPoolWithdrawalConfig.requestEpochsInAdvance,
439
468
  };
440
469
 
441
- // show only enabled pools from cms
470
+ // When Directus is available, show only enabled pools.
471
+ // When Directus is unavailable, include all pools.
442
472
  if (poolOverview.enabled) {
443
473
  retn.push(poolOverview);
444
474
  }
@@ -797,9 +827,14 @@ export class DataService {
797
827
  );
798
828
  const retn: PoolRepayment[] = [];
799
829
  for (const data of poolRepaymentDirectus) {
830
+ // Find subgraph pool that maps to this Directus pool
831
+ // On non-Base chains, subgraph pool IDs are different from Directus poolIdFK
832
+ // Use getMetadataPoolId to map subgraph pool ID -> Directus/Base pool ID
800
833
  const lendingPoolSubgraph =
801
834
  lendingPoolsWithdrawalsAndDepositsSubgraph.lendingPools.find(
802
- (pool) => pool.id == data.poolIdFK,
835
+ (pool) =>
836
+ this.getMetadataPoolId(pool.id) ===
837
+ data.poolIdFK.toLowerCase(),
803
838
  );
804
839
 
805
840
  if (lendingPoolSubgraph === undefined) {
@@ -949,14 +984,19 @@ export class DataService {
949
984
  retn.totalYieldEarned += parseFloat(poolOverview.yieldEarned);
950
985
  }
951
986
  for (const poolOverview of poolOverviews) {
987
+ // Map to metadata pool ID for Directus lookup (XDC pools → Base pools)
988
+ const metadataPoolId = this.getMetadataPoolId(poolOverview.id);
952
989
  const riskManagement = riskManagements.find(
953
990
  (rm) =>
954
- rm.poolIdFK.toLowerCase() === poolOverview.id.toLowerCase(),
991
+ rm.poolIdFK.toLowerCase() === metadataPoolId.toLowerCase(),
955
992
  );
956
993
  if (!riskManagement) {
957
994
  console.warn(
958
995
  "Couldn't find risk management for id: ",
959
996
  poolOverview.id,
997
+ metadataPoolId !== poolOverview.id
998
+ ? `(mapped to ${metadataPoolId})`
999
+ : '',
960
1000
  );
961
1001
  continue;
962
1002
  }
@@ -1,14 +1,5 @@
1
1
  import { gql } from 'graphql-request';
2
2
 
3
- export const getPlumeTvl = gql`
4
- {
5
- lendingPools {
6
- id
7
- balance
8
- }
9
- }
10
- `;
11
-
12
3
  export const getPoolOverviewQuery = (ids?: string[]): string => gql`
13
4
  query getAllPoolOverview($epochId: String!, $unusedPools: [String]!) {
14
5
  lendingPools(
@@ -16,13 +16,6 @@ export interface LendingPoolSubgraph {
16
16
  configuration: LendingPoolConfigurationSubgraph;
17
17
  }
18
18
 
19
- export interface PlumeSubgraphReturn {
20
- lendingPools: {
21
- id: string;
22
- balance: string;
23
- }[];
24
- }
25
-
26
19
  export interface LendingPoolSubgraphReturn {
27
20
  lendingPools: LendingPoolSubgraph[];
28
21
  }
@@ -19,7 +19,6 @@ export interface PoolOverview {
19
19
  totalValueLocked: {
20
20
  total: string;
21
21
  offchain: string;
22
- plume: string;
23
22
  };
24
23
  loansUnderManagement: string;
25
24
  yieldEarned: string;