@kasufinance/kasu-sdk 1.0.4 → 2.1.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 (77) hide show
  1. package/.claude/settings.local.json +15 -0
  2. package/CLAUDE.md +410 -0
  3. package/README.md +179 -12
  4. package/dist/bundle.cjs.js +616 -67
  5. package/dist/bundle.esm.js +612 -69
  6. package/dist/facade/chain-configs.d.ts +11 -0
  7. package/dist/facade/chain-configs.js +84 -0
  8. package/dist/facade/chain-configs.js.map +1 -0
  9. package/dist/facade/deposits.d.ts +56 -0
  10. package/dist/facade/deposits.js +87 -0
  11. package/dist/facade/deposits.js.map +1 -0
  12. package/dist/facade/index.d.ts +6 -0
  13. package/dist/facade/index.js +9 -0
  14. package/dist/facade/index.js.map +1 -0
  15. package/dist/facade/kasu.d.ts +71 -0
  16. package/dist/facade/kasu.js +101 -0
  17. package/dist/facade/kasu.js.map +1 -0
  18. package/dist/facade/strategies.d.ts +37 -0
  19. package/dist/facade/strategies.js +112 -0
  20. package/dist/facade/strategies.js.map +1 -0
  21. package/dist/facade/types.d.ts +127 -0
  22. package/dist/facade/types.js +2 -0
  23. package/dist/facade/types.js.map +1 -0
  24. package/dist/facade/user-portfolio.d.ts +35 -0
  25. package/dist/facade/user-portfolio.js +53 -0
  26. package/dist/facade/user-portfolio.js.map +1 -0
  27. package/dist/index.d.ts +9 -17
  28. package/dist/index.js +9 -16
  29. package/dist/index.js.map +1 -1
  30. package/dist/kasu-sdk.d.ts +17 -0
  31. package/dist/kasu-sdk.js +17 -0
  32. package/dist/kasu-sdk.js.map +1 -0
  33. package/dist/sdk-config.d.ts +28 -4
  34. package/dist/sdk-config.js +8 -6
  35. package/dist/sdk-config.js.map +1 -1
  36. package/dist/services/DataService/data-service.d.ts +9 -2
  37. package/dist/services/DataService/data-service.js +90 -52
  38. package/dist/services/DataService/data-service.js.map +1 -1
  39. package/dist/services/DataService/queries.d.ts +0 -1
  40. package/dist/services/DataService/queries.js +0 -8
  41. package/dist/services/DataService/queries.js.map +1 -1
  42. package/dist/services/DataService/subgraph-types.d.ts +0 -6
  43. package/dist/services/DataService/types.d.ts +0 -1
  44. package/dist/services/Locking/locking.d.ts +4 -2
  45. package/dist/services/Locking/locking.js +89 -7
  46. package/dist/services/Locking/locking.js.map +1 -1
  47. package/dist/services/Portfolio/portfolio.d.ts +1 -0
  48. package/dist/services/Portfolio/portfolio.js +9 -1
  49. package/dist/services/Portfolio/portfolio.js.map +1 -1
  50. package/dist/tests/facade.test.d.ts +1 -0
  51. package/dist/tests/facade.test.js +196 -0
  52. package/dist/tests/facade.test.js.map +1 -0
  53. package/dist/tests/sample.test.js +19 -14
  54. package/dist/tests/sample.test.js.map +1 -1
  55. package/dist/utils/deployment-mode.d.ts +6 -0
  56. package/dist/utils/deployment-mode.js +8 -0
  57. package/dist/utils/deployment-mode.js.map +1 -0
  58. package/package.json +2 -2
  59. package/src/facade/chain-configs.ts +93 -0
  60. package/src/facade/deposits.ts +102 -0
  61. package/src/facade/index.ts +26 -0
  62. package/src/facade/kasu.ts +144 -0
  63. package/src/facade/strategies.ts +119 -0
  64. package/src/facade/types.ts +168 -0
  65. package/src/facade/user-portfolio.ts +73 -0
  66. package/src/index.ts +59 -25
  67. package/src/kasu-sdk.ts +27 -0
  68. package/src/sdk-config.ts +36 -15
  69. package/src/services/DataService/data-service.ts +105 -65
  70. package/src/services/DataService/queries.ts +0 -9
  71. package/src/services/DataService/subgraph-types.ts +0 -7
  72. package/src/services/DataService/types.ts +0 -1
  73. package/src/services/Locking/locking.ts +113 -12
  74. package/src/services/Portfolio/portfolio.ts +15 -5
  75. package/src/tests/facade.test.ts +245 -0
  76. package/src/tests/sample.test.ts +7 -9
  77. package/src/utils/deployment-mode.ts +9 -0
@@ -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[],
@@ -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;
@@ -57,29 +57,37 @@ import {
57
57
 
58
58
  export class KSULocking {
59
59
  private readonly _contractAbi: IKSULockingAbi;
60
- private readonly _kasuToken: IERC20MetadataAbi;
60
+ private readonly _kasuToken: IERC20MetadataAbi | null;
61
61
  private readonly _kasuBonusAddress: string;
62
62
  private readonly _graph: GraphQLClient;
63
63
  private readonly _userManagerAbi: IUserManagerAbi;
64
64
  private readonly _systemVariablesAbi: ISystemVariablesAbi;
65
65
  private readonly _ksuPriceAbi: IKsuPriceAbi;
66
66
  private readonly _userLoyaltyRewardsAbi: IUserLoyaltyRewardsAbi;
67
+ private readonly _isLiteDeployment: boolean;
67
68
  private readonly apyBonuses: number[] = [0, 0.001, 0.002];
68
- /**
69
- *
70
- */
69
+
71
70
  constructor(
72
71
  private _kasuConfig: SdkConfig,
73
72
  signerOrProvider: Signer | Provider,
74
73
  ) {
74
+ this._isLiteDeployment = _kasuConfig.isLiteDeployment;
75
+
75
76
  this._contractAbi = IKSULockingAbi__factory.connect(
76
77
  _kasuConfig.contracts.IKSULocking,
77
78
  signerOrProvider,
78
79
  );
79
- this._kasuToken = IERC20MetadataAbi__factory.connect(
80
- _kasuConfig.contracts.KSUToken,
81
- signerOrProvider,
82
- );
80
+
81
+ // Only initialize KSU token contract if not a Lite deployment and address is provided
82
+ if (!this._isLiteDeployment && _kasuConfig.contracts.KSUToken) {
83
+ this._kasuToken = IERC20MetadataAbi__factory.connect(
84
+ _kasuConfig.contracts.KSUToken,
85
+ signerOrProvider,
86
+ );
87
+ } else {
88
+ this._kasuToken = null;
89
+ }
90
+
83
91
  this._userManagerAbi = IUserManagerAbi__factory.connect(
84
92
  _kasuConfig.contracts.UserManager,
85
93
  signerOrProvider,
@@ -100,10 +108,20 @@ export class KSULocking {
100
108
  );
101
109
  }
102
110
 
111
+ /**
112
+ * Check if this is a Lite deployment (no KSU token functionality)
113
+ */
114
+ get isLiteDeployment(): boolean {
115
+ return this._isLiteDeployment;
116
+ }
117
+
103
118
  async lockKSUTokens(
104
119
  amount: BigNumber,
105
120
  lockPeriod: BigNumber,
106
121
  ): Promise<ContractTransaction> {
122
+ if (this._isLiteDeployment) {
123
+ throw new Error('Locking is not available on Lite deployments');
124
+ }
107
125
  return this._contractAbi.lock(amount, lockPeriod);
108
126
  }
109
127
 
@@ -112,6 +130,10 @@ export class KSULocking {
112
130
  lockPeriod: number,
113
131
  permit: RSVDeadlineValue,
114
132
  ): Promise<ContractTransaction> {
133
+ if (this._isLiteDeployment) {
134
+ throw new Error('Locking is not available on Lite deployments');
135
+ }
136
+
115
137
  const amountBN = BigNumber.from(amount);
116
138
  const lockPeriodBN = BigNumber.from(lockPeriod);
117
139
 
@@ -134,13 +156,27 @@ export class KSULocking {
134
156
  amount: BigNumber,
135
157
  userLockId: BigNumber,
136
158
  ): Promise<ContractTransaction> {
159
+ if (this._isLiteDeployment) {
160
+ throw new Error('Locking is not available on Lite deployments');
161
+ }
137
162
  return this._contractAbi.unlock(amount, userLockId);
138
163
  }
139
164
 
140
165
  async claimFees(): Promise<ContractTransaction> {
166
+ if (this._isLiteDeployment) {
167
+ throw new Error('Fee claiming is not available on Lite deployments');
168
+ }
141
169
  return await this._contractAbi.claimFees();
142
170
  }
171
+
143
172
  async lockDetails(lockPeriod: BigNumber): Promise<LockPeriodInterface> {
173
+ if (this._isLiteDeployment) {
174
+ return {
175
+ rKsuMultiplier: 0,
176
+ ksuBonusMultiplier: 0,
177
+ isActive: false,
178
+ };
179
+ }
144
180
  const result = await this._contractAbi.lockDetails(lockPeriod);
145
181
  return {
146
182
  rKsuMultiplier: result[0].toNumber(),
@@ -150,6 +186,9 @@ export class KSULocking {
150
186
  }
151
187
 
152
188
  async userTotalDeposits(userAddress: string): Promise<BigNumber> {
189
+ if (this._isLiteDeployment) {
190
+ return BigNumber.from(0);
191
+ }
153
192
  return await this._contractAbi.userTotalDeposits(userAddress);
154
193
  }
155
194
 
@@ -158,7 +197,7 @@ export class KSULocking {
158
197
  KSULocked: number,
159
198
  lockPeriod: BigNumber,
160
199
  ): Promise<number> {
161
- if (KSULocked === 0) {
200
+ if (this._isLiteDeployment || KSULocked === 0) {
162
201
  return 0;
163
202
  }
164
203
 
@@ -232,6 +271,10 @@ export class KSULocking {
232
271
  }
233
272
 
234
273
  async getUserLocks(userAddress: string): Promise<UserLock[]> {
274
+ if (this._isLiteDeployment) {
275
+ return [];
276
+ }
277
+
235
278
  const result: GQLUserLocks = await this._graph.request(userLocksQuery, {
236
279
  userAddress: userAddress.toLowerCase(),
237
280
  });
@@ -261,8 +304,14 @@ export class KSULocking {
261
304
  }
262
305
 
263
306
  async getUserBonusData(userAddress: string): Promise<UserBonusData> {
264
- // TODO this userLocks query is kinda bad -> change to userLocksDepositInfo as u never need user locks by themselves
265
- // whole function takes between 500 - 800ms
307
+ if (this._isLiteDeployment) {
308
+ return {
309
+ ksuBonusAndRewards: '0',
310
+ ksuBonusAndRewardsLifetime: '0',
311
+ protocolFeesEarned: '0',
312
+ totalLockedAmount: '0',
313
+ };
314
+ }
266
315
 
267
316
  const DECIMALS = 18;
268
317
 
@@ -322,6 +371,11 @@ export class KSULocking {
322
371
  loyaltyLevel: number;
323
372
  apyBonus: number;
324
373
  } {
374
+ // On Lite deployments, always return level 0 with no bonus
375
+ if (this._isLiteDeployment) {
376
+ return { loyaltyLevel: 0, apyBonus: 0 };
377
+ }
378
+
325
379
  if (rKSUtoUSDCRatio < 0.01) {
326
380
  return { loyaltyLevel: 0, apyBonus: this.apyBonuses[0] };
327
381
  } else if (rKSUtoUSDCRatio < 0.05) {
@@ -334,6 +388,10 @@ export class KSULocking {
334
388
  rKSUAmount: string,
335
389
  userAddress: string,
336
390
  ): Promise<number> {
391
+ if (this._isLiteDeployment) {
392
+ return 0;
393
+ }
394
+
337
395
  const [depositedAmount, pendingAmount]: [BigNumber, BigNumber] =
338
396
  await this._userManagerAbi.userTotalPendingAndActiveDepositedAmountForCurrentEpoch(
339
397
  userAddress,
@@ -357,6 +415,9 @@ export class KSULocking {
357
415
  }
358
416
 
359
417
  async getClaimableRewards(userAddress: string): Promise<BigNumber> {
418
+ if (this._isLiteDeployment) {
419
+ return BigNumber.from(0);
420
+ }
360
421
  const result = await this._contractAbi.rewards(userAddress);
361
422
  return result;
362
423
  }
@@ -364,6 +425,7 @@ export class KSULocking {
364
425
  async getKasuTokenPrice(): Promise<{ price: BigNumber; decimals: number }> {
365
426
  const decimals = 18;
366
427
 
428
+ // Price can still be queried on Lite deployments (set to 0 or configured value)
367
429
  return Promise.resolve({
368
430
  price: await this._ksuPriceAbi.ksuTokenPrice(),
369
431
  decimals,
@@ -387,6 +449,10 @@ export class KSULocking {
387
449
  rewardDecimals: number,
388
450
  claimableRewards?: BigNumber,
389
451
  ): Promise<BigNumber> {
452
+ if (this._isLiteDeployment) {
453
+ return BigNumber.from(0);
454
+ }
455
+
390
456
  const result: GQLClaimedFeesForAddress = await this._graph.request(
391
457
  claimedFeesQuery,
392
458
  {
@@ -408,6 +474,10 @@ export class KSULocking {
408
474
  bonusMultiplier: number,
409
475
  bonusTokensLeft: BigNumber,
410
476
  ): BigNumber {
477
+ if (this._isLiteDeployment) {
478
+ return BigNumber.from(0);
479
+ }
480
+
411
481
  const bonusMultiplierBN = ethers.utils.parseUnits(
412
482
  bonusMultiplier.toFixed(2),
413
483
  2,
@@ -422,6 +492,10 @@ export class KSULocking {
422
492
  }
423
493
 
424
494
  async getUserStakedKsu(userAddress: string): Promise<string> {
495
+ if (this._isLiteDeployment) {
496
+ return '0';
497
+ }
498
+
425
499
  const result: GQLStakedAmountForAddress = await this._graph.request(
426
500
  userStakedKsuQuery,
427
501
  {
@@ -435,6 +509,10 @@ export class KSULocking {
435
509
  }
436
510
 
437
511
  async getUserEarnedrKsu(userAddress: string): Promise<string> {
512
+ if (this._isLiteDeployment) {
513
+ return '0';
514
+ }
515
+
438
516
  const result: GQLEarnedRKsuForAddress = await this._graph.request(
439
517
  userEarnedrKsuQuery,
440
518
  {
@@ -449,6 +527,10 @@ export class KSULocking {
449
527
  }
450
528
 
451
529
  async getUserTotalBonusAmount(userAddress: string): Promise<string> {
530
+ if (this._isLiteDeployment) {
531
+ return '0';
532
+ }
533
+
452
534
  const result: GQLTotalBonusAmountForAddress = await this._graph.request(
453
535
  userTotalBonusAmountQuery,
454
536
  {
@@ -465,6 +547,13 @@ export class KSULocking {
465
547
  async getLockingRewards(
466
548
  userAddress: string,
467
549
  ): Promise<{ claimableRewards: string; lifeTimeRewards: string }> {
550
+ if (this._isLiteDeployment) {
551
+ return {
552
+ claimableRewards: '0',
553
+ lifeTimeRewards: '0',
554
+ };
555
+ }
556
+
468
557
  const rewardDecimals = 6;
469
558
 
470
559
  const claimableRewards = await this.getClaimableRewards(userAddress);
@@ -486,18 +575,26 @@ export class KSULocking {
486
575
  }
487
576
 
488
577
  async getAvailableKsuBonus(): Promise<string> {
489
- const amount = await this._kasuToken.balanceOf(this._kasuBonusAddress);
578
+ if (this._isLiteDeployment || !this._kasuToken) {
579
+ return '0';
580
+ }
490
581
 
582
+ const amount = await this._kasuToken.balanceOf(this._kasuBonusAddress);
491
583
  return formatEther(amount);
492
584
  }
493
585
 
494
586
  async getUserKsuBalance(userAddress: string): Promise<string> {
587
+ if (this._isLiteDeployment || !this._kasuToken) {
588
+ return '0';
589
+ }
590
+
495
591
  const balance = await this._kasuToken.balanceOf(
496
592
  userAddress.toLowerCase(),
497
593
  );
498
594
 
499
595
  return formatEther(balance);
500
596
  }
597
+
501
598
  async getNextEpochDate(): Promise<EpochTimeStamp> {
502
599
  return (
503
600
  await this._systemVariablesAbi.nextEpochStartTimestamp()
@@ -526,6 +623,10 @@ export class KSULocking {
526
623
  }
527
624
 
528
625
  async getActiveLockPeriods(): Promise<LockPeriod[]> {
626
+ if (this._isLiteDeployment) {
627
+ return [];
628
+ }
629
+
529
630
  const data: GQLGetLockingPeriods =
530
631
  await this._graph.request(lockingPeriodsQuery);
531
632
  return data.lockPeriods;
@@ -11,6 +11,7 @@ import {
11
11
  IUserLoyaltyRewardsAbi__factory,
12
12
  } from '../../contracts';
13
13
  import { SdkConfig } from '../../sdk-config';
14
+ import { isLiteDeployment } from '../../utils/deployment-mode';
14
15
  import { DataService } from '../DataService/data-service';
15
16
  import {
16
17
  getAllTrancheConfigurationsQuery,
@@ -53,8 +54,9 @@ export class Portfolio {
53
54
  private _userLoyaltyRewardsAbi: ReturnType<
54
55
  typeof IUserLoyaltyRewardsAbi__factory.connect
55
56
  >;
56
- private _kasuNftContract: IKasuNFTsAbi;
57
+ private _kasuNftContract: IKasuNFTsAbi | null;
57
58
  readonly _signerOrProvider: Signer | Provider;
59
+ private _isLiteDeployment: boolean;
58
60
 
59
61
  constructor(
60
62
  private _kasuConfig: SdkConfig,
@@ -75,10 +77,13 @@ export class Portfolio {
75
77
  _kasuConfig,
76
78
  signerOrProvider,
77
79
  );
78
- this._kasuNftContract = IKasuNFTsAbi__factory.connect(
79
- _kasuConfig.contracts.KasuNFTs,
80
- signerOrProvider,
81
- );
80
+ this._isLiteDeployment = isLiteDeployment(_kasuConfig);
81
+ this._kasuNftContract = _kasuConfig.contracts.KasuNFTs
82
+ ? IKasuNFTsAbi__factory.connect(
83
+ _kasuConfig.contracts.KasuNFTs,
84
+ signerOrProvider,
85
+ )
86
+ : null;
82
87
  this._graph = new GraphQLClient(_kasuConfig.subgraphUrl);
83
88
  }
84
89
 
@@ -293,6 +298,11 @@ export class Portfolio {
293
298
  }
294
299
 
295
300
  async getUserNfts(userAddress: string): Promise<number[]> {
301
+ // NFTs are not available on Lite deployments
302
+ if (this._isLiteDeployment || !this._kasuNftContract) {
303
+ return [];
304
+ }
305
+
296
306
  const usernfts = await this._kasuNftContract.tokensOfOwner(userAddress);
297
307
 
298
308
  return usernfts.map((nft) => nft.toNumber());