@strkfarm/sdk 2.0.0-staging.4 → 2.0.0-staging.41

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.
@@ -1,5 +1,6 @@
1
1
  import { logger } from "@/utils/logger";
2
2
  import BigNumber from "bignumber.js";
3
+ import { uint256 } from "starknet";
3
4
 
4
5
  export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
5
6
  decimals: number;
@@ -107,6 +108,10 @@ export class _Web3Number<T extends _Web3Number<T>> extends BigNumber {
107
108
  sign: sign,
108
109
  }
109
110
  }
111
+
112
+ toUint256() {
113
+ return uint256.bnToUint256(this.toWei());
114
+ }
110
115
  }
111
116
 
112
117
  BigNumber.config({ DECIMAL_PLACES: 18, ROUNDING_MODE: BigNumber.ROUND_DOWN });
@@ -1,3 +1,4 @@
1
+ import { Uint256, uint256 } from "starknet";
1
2
  import { _Web3Number } from "./_bignumber";
2
3
 
3
4
  export class Web3Number extends _Web3Number<Web3Number> {
@@ -5,4 +6,8 @@ export class Web3Number extends _Web3Number<Web3Number> {
5
6
  const bn = (new Web3Number(weiNumber, decimals)).dividedBy(10 ** decimals)
6
7
  return new Web3Number(bn.toString(), decimals);
7
8
  }
9
+
10
+ static fromUint256(uint256Value: Uint256): Web3Number {
11
+ return this.fromWei(uint256.uint256ToBN(uint256Value).toString(), 18);
12
+ }
8
13
  }
@@ -1,5 +1,6 @@
1
1
  import util from 'util';
2
2
  import { _Web3Number } from "./_bignumber";
3
+ import { Uint256, uint256 } from 'starknet';
3
4
 
4
5
  export class Web3Number extends _Web3Number<Web3Number> {
5
6
 
@@ -8,6 +9,10 @@ export class Web3Number extends _Web3Number<Web3Number> {
8
9
  return new Web3Number(bn.toString(), decimals);
9
10
  }
10
11
 
12
+ static fromUint256(uint256Value: Uint256): Web3Number {
13
+ return this.fromWei(uint256.uint256ToBN(uint256Value).toString(), 18);
14
+ }
15
+
11
16
  [util.inspect.custom](depth: any, opts: any): string {
12
17
  return this.toString();
13
18
  }
package/src/global.ts CHANGED
@@ -165,6 +165,33 @@ const defaultTokens: TokenInfo[] = [{
165
165
  coingeckId: undefined,
166
166
  displayDecimals: 2,
167
167
  priceCheckAmount: 100,
168
+ }, {
169
+ name: "fyeWBTC",
170
+ symbol: "fyeWBTC",
171
+ logo: 'https://assets.strkfarm.com/integrations/tokens/wbtc.svg',
172
+ address: ContractAddr.from('0x04dd39de0a588f5e1c7a8377e1bef2c49caaee49a11433429d2c48f587b3a492'),
173
+ decimals: 8,
174
+ coingeckId: undefined,
175
+ displayDecimals: 6,
176
+ priceCheckAmount: 0.001, // 112000 * 0.0001 = $110.2
177
+ }, {
178
+ name: "fyETH",
179
+ symbol: "fyETH",
180
+ logo: 'https://assets.strkfarm.com/integrations/tokens/eth.svg',
181
+ address: ContractAddr.from('0x050707bC3b8730022F10530C2c6f6b9467644129C50C2868Ad0036c5e4E9e616'),
182
+ decimals: 18,
183
+ coingeckId: undefined,
184
+ displayDecimals: 4,
185
+ priceCheckAmount: 0.1,
186
+ }, {
187
+ name: "fyeUSDC",
188
+ symbol: "fyeUSDC",
189
+ logo: 'https://assets.strkfarm.com/integrations/tokens/usdc.svg',
190
+ address: ContractAddr.from('0x07fdcec0cef01294c9c3d52415215949805c77bae8003702a7928fd6d2c36bc1'),
191
+ decimals: 6,
192
+ coingeckId: undefined,
193
+ displayDecimals: 2,
194
+ priceCheckAmount: 100,
168
195
  }]
169
196
  const tokens: TokenInfo[] = defaultTokens;
170
197
 
@@ -55,21 +55,24 @@ export interface IProtocol {
55
55
  logo: string;
56
56
  }
57
57
 
58
- // Strategy-level enums
59
- export enum StrategyCategory {
60
- ALL = "all",
61
- BTC = "btc",
62
- META_VAULTS = "meta-vaults",
58
+ export interface ICurator {
59
+ name: string;
60
+ logo: string;
63
61
  }
64
62
 
65
63
  export enum StrategyTag {
66
- EKUBO = "Ekubo",
67
- EVERGREEN = "Evergreen",
68
- HYPER_LST = "Hyper-LST",
69
- VESU = "Vesu",
70
- SENSEI = "Sensei",
71
- ENDUR = "Endur",
72
- BTC = "BTC"
64
+ META_VAULT = "Meta Vaults",
65
+ LEVERED = "Maxx",
66
+ AUTOMATED_LP = "Ekubo",
67
+ BTC = "BTC"
68
+ }
69
+
70
+ export enum VaultType {
71
+ LOOPING = "Looping",
72
+ META_VAULT = "Meta Vault",
73
+ DELTA_NEUTRAL = "Delta Neutral",
74
+ AUTOMATED_LP = "Automated LP",
75
+ TVA = "Troves Value Averaging",
73
76
  }
74
77
 
75
78
  // Security metadata enums
@@ -86,6 +89,7 @@ export enum SourceCodeType {
86
89
  export enum AccessControlType {
87
90
  MULTISIG_ACCOUNT = "Multisig Account",
88
91
  STANDARD_ACCOUNT = "Standard Account",
92
+ ROLE_BASED_ACCESS = "Role Based Access",
89
93
  }
90
94
 
91
95
  export enum InstantWithdrawalVault {
@@ -102,7 +106,7 @@ export interface SourceCodeInfo {
102
106
  export interface AccessControlInfo {
103
107
  type: AccessControlType;
104
108
  addresses: ContractAddr[];
105
- timeLock: string;
109
+ timeLock?: string;
106
110
  }
107
111
 
108
112
  export interface SecurityMetadata {
@@ -111,16 +115,13 @@ export interface SecurityMetadata {
111
115
  accessControl: AccessControlInfo;
112
116
  }
113
117
 
114
- // Redemption metadata interfaces
115
- export interface RedemptionExpectedTime {
116
- upto1M: string;
117
- upto10M: string;
118
- above10M: string;
119
- }
120
-
121
118
  export interface RedemptionInfo {
122
119
  instantWithdrawalVault: InstantWithdrawalVault;
123
- expectedRedemptionTime?: RedemptionExpectedTime;
120
+ redemptionsInfo: {
121
+ title: string; // e.g. Upto $1M
122
+ description: string; // e.g. "1-2 hours"
123
+ }[],
124
+ alerts: StrategyAlert[];
124
125
  }
125
126
 
126
127
  export enum FlowChartColors {
@@ -138,7 +139,8 @@ export enum StrategyLiveStatus {
138
139
  ACTIVE = "Active",
139
140
  NEW = "New",
140
141
  COMING_SOON = "Coming Soon",
141
- RETIRED = "Retired",
142
+ DEPRECATED = "Deprecated", // active but not recommended
143
+ RETIRED = "Retired", // not active anymore
142
144
  HOT = "Hot & New 🔥"
143
145
  }
144
146
 
@@ -165,6 +167,13 @@ export interface StrategySettings {
165
167
  tags?: StrategyTag[];
166
168
  }
167
169
 
170
+ export interface StrategyApyHistoryUIConfig {
171
+ // Defaults to true in UI if omitted.
172
+ showApyHistory?: boolean;
173
+ // Optional message shown when APY history is hidden.
174
+ noApyHistoryMessage?: string;
175
+ }
176
+
168
177
  /**
169
178
  * @property risk.riskFactor.factor - The risk factors that are considered for the strategy.
170
179
  * @property risk.riskFactor.factor - The value of the risk factor from 0 to 10, 0 being the lowest and 10 being the highest.
@@ -178,6 +187,10 @@ export interface IStrategyMetadata<T> {
178
187
  address: ContractAddr;
179
188
  launchBlock: number;
180
189
  type: "ERC4626" | "ERC721" | "Other";
190
+ vaultType: {
191
+ type: VaultType;
192
+ description: string;
193
+ };
181
194
  depositTokens: TokenInfo[];
182
195
  protocols: IProtocol[];
183
196
  auditUrl?: string;
@@ -187,6 +200,7 @@ export interface IStrategyMetadata<T> {
187
200
  notARisks: RiskType[];
188
201
  };
189
202
  apyMethodology?: string;
203
+ realizedApyMethodology?: string;
190
204
  additionalInfo: T;
191
205
  contractDetails: {
192
206
  address: ContractAddr;
@@ -197,13 +211,20 @@ export interface IStrategyMetadata<T> {
197
211
  points?: {multiplier: number, logo: string, toolTip?: string}[];
198
212
  docs?: string;
199
213
  investmentSteps: string[];
200
- curator?: { name: string, logo: string },
214
+ curator?: ICurator,
201
215
  isPreview?: boolean;
202
- category: StrategyCategory;
203
216
  tags?: StrategyTag[];
204
217
  security: SecurityMetadata;
205
218
  redemptionInfo: RedemptionInfo;
219
+ usualTimeToEarnings: null | string; // e.g. "2 weeks" // some strats grow like step functions
220
+ usualTimeToEarningsDescription: null | string; // e.g. "LSTs price on DEX goes up roughly every 2 weeks"
221
+ discontinuationInfo?: {
222
+ date?: Date;
223
+ reason?: React.ReactNode | string;
224
+ info?: React.ReactNode | string;
225
+ };
206
226
  settings?: StrategySettings;
227
+ apyHistoryUIConfig?: StrategyApyHistoryUIConfig;
207
228
  // Legacy field for multi-step strategies (deprecated, use investmentFlows instead)
208
229
  actions?: Array<{
209
230
  name?: string;
@@ -242,6 +263,23 @@ export function getMainnetConfig(
242
263
  };
243
264
  }
244
265
 
266
+ export const getStrategyTagDesciption = (tag: StrategyTag): string => {
267
+ switch (tag) {
268
+ case StrategyTag.META_VAULT:
269
+ return "A meta vault is a vault that auto allocates funds to multiple vaults based on optimal yield opportunities";
270
+ case StrategyTag.LEVERED:
271
+ return "Looping vaults on Endur LSTs with leveraged borrowing of STRK or BTC to increase yield (2-4x higher yield than simply staking)";
272
+ case StrategyTag.AUTOMATED_LP:
273
+ return "Automated LP vaults on Ekubo that rebalance position automatically, ensuring you earn fees efficiently";
274
+ case StrategyTag.BTC:
275
+ return "BTC linked vaults";
276
+ }
277
+ }
278
+
279
+ export const getAllStrategyTags = (): StrategyTag[] => {
280
+ return Object.values(StrategyTag);
281
+ }
282
+
245
283
  export const getRiskExplaination = (riskType: RiskType) => {
246
284
  switch (riskType) {
247
285
  case RiskType.MARKET_RISK:
@@ -314,7 +352,7 @@ export function highlightTextWithLinks(
314
352
  {parts.map((part, i) => {
315
353
  const match = highlights.find(m => m.highlight.toLowerCase() === part.toLowerCase());
316
354
  return match ? (
317
- <a key={i} href={match.link} target="_blank" style={{ color: 'var(--chakra-colors-white)', background: 'var(--chakra-colors-highlight)' }}>
355
+ <a key={i} href={match.link} target="_blank" style={{ color: 'white', background: 'rgba(255, 255, 255, 0.04)' }}>
318
356
  {part}
319
357
  </a>
320
358
  ) : (
@@ -375,4 +413,9 @@ export const Protocols = {
375
413
  VESU: VesuProtocol,
376
414
  ENDUR: EndurProtocol,
377
415
  EXTENDED: ExtendedProtocol
416
+ }
417
+
418
+ export const UnwrapLabsCurator: ICurator = {
419
+ name: "Unwrap Labs",
420
+ logo: "https://assets.troves.fi/integrations/unwraplabs/white.png"
378
421
  }
@@ -37,7 +37,7 @@ export class AvnuWrapper {
37
37
  excludeSources = ['Haiko(Solvers)']
38
38
  ): Promise<Quote> {
39
39
  const MAX_RETRY = 5;
40
- // logger.verbose(`${AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
40
+ logger.verbose(`${AvnuWrapper.name}: getQuotes => Getting quotes for ${fromToken} -> ${toToken}, amount: ${amountWei}, taker: ${taker}, retry: ${retry}`);
41
41
  const params: any = {
42
42
  sellTokenAddress: fromToken,
43
43
  buyTokenAddress: toToken,
@@ -2,7 +2,7 @@ import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { IConfig } from "@/interfaces";
3
3
  import { Contract } from "starknet";
4
4
  import ERC20Abi from '@/data/erc20.abi.json';
5
-
5
+ import { uint256 } from "starknet";
6
6
  export class ERC20 {
7
7
  readonly config: IConfig;
8
8
 
@@ -12,7 +12,7 @@ export class ERC20 {
12
12
 
13
13
  contract(addr: string | ContractAddr) {
14
14
  const _addr = typeof addr === 'string' ? addr : addr.address;
15
- return new Contract({abi: ERC20Abi, address: _addr, providerOrAccount: this.config.provider});
15
+ return new Contract({ abi: ERC20Abi, address: _addr, providerOrAccount: this.config.provider });
16
16
  }
17
17
 
18
18
  async balanceOf(token: string | ContractAddr, address: string | ContractAddr, tokenDecimals: number) {
@@ -26,4 +26,20 @@ export class ERC20 {
26
26
  const allowance = await contract.call('allowance', [owner.toString(), spender.toString()]);
27
27
  return Web3Number.fromWei(allowance.toString(), tokenDecimals);
28
28
  }
29
+
30
+ approve(
31
+ token: string | ContractAddr,
32
+ spender: string | ContractAddr,
33
+ amount: Web3Number
34
+ ) {
35
+ const contract = this.contract(token);
36
+ const amountUint256 = uint256.bnToUint256(amount.toWei());
37
+ const approveCall = contract.populate("approve", [
38
+ spender.toString(),
39
+ amountUint256.low.toString(),
40
+ amountUint256.high.toString(),
41
+ ]);
42
+ return approveCall;
43
+ }
44
+
29
45
  }
@@ -23,18 +23,76 @@ export interface DualTokenInfo {
23
23
  token1: SingleTokenInfo
24
24
  }
25
25
 
26
+ export type StrategyInputMode = "single" | "dual";
27
+ export type InputModeFromAction<T> = T extends DualActionAmount ? "dual" : "single";
28
+
29
+ export interface NetAPYSplit {
30
+ apy: number;
31
+ id: string;
32
+ }
33
+
34
+ export interface NetAPYDetails {
35
+ net: number;
36
+ splits: NetAPYSplit[];
37
+ }
38
+
39
+ export type UserPositionCardSubValueColor = "default" | "positive" | "negative" | "info";
40
+
41
+ export interface UserPositionCard {
42
+ title: string;
43
+ value: string;
44
+ tooltip?: string;
45
+ subValue?: string;
46
+ subValueColor?: UserPositionCardSubValueColor;
47
+ kind?: "metric" | "apy";
48
+ apyMethod?: string;
49
+ }
50
+
51
+ export interface UserPositionCardsInput {
52
+ user: ContractAddr;
53
+ investmentFlows?: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>;
54
+ usualTimeToEarnings?: string | null;
55
+ usualTimeToEarningsDescription?: string | null;
56
+ apy?: number | string | null;
57
+ apyMethod?: string | null;
58
+ }
59
+
26
60
  interface CacheData {
27
61
  timestamp: number;
28
62
  ttl: number;
29
63
  data: any;
30
64
  }
31
- export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
32
- readonly config: IConfig;
33
- readonly cache: Map<string, CacheData> = new Map();
65
+ export class BaseStrategy<
66
+ TVLInfo,
67
+ DepositActionInfo,
68
+ WithdrawActionInfo = DepositActionInfo,
69
+ > extends CacheClass {
70
+ readonly config: IConfig;
71
+ readonly cache: Map<string, CacheData> = new Map();
72
+ private readonly _depositInputMode: InputModeFromAction<DepositActionInfo>;
73
+ private readonly _withdrawInputMode: InputModeFromAction<WithdrawActionInfo>;
34
74
 
35
- constructor(config: IConfig) {
75
+ constructor(
76
+ config: IConfig,
77
+ inputModes?: {
78
+ depositInputMode?: InputModeFromAction<DepositActionInfo>;
79
+ withdrawInputMode?: InputModeFromAction<WithdrawActionInfo>;
80
+ }
81
+ ) {
36
82
  super();
37
83
  this.config = config;
84
+ this._depositInputMode = (inputModes?.depositInputMode ??
85
+ ("single" as InputModeFromAction<DepositActionInfo>));
86
+ this._withdrawInputMode = (inputModes?.withdrawInputMode ??
87
+ ("single" as InputModeFromAction<WithdrawActionInfo>));
88
+ }
89
+
90
+ depositInputMode(): InputModeFromAction<DepositActionInfo> {
91
+ return this._depositInputMode;
92
+ }
93
+
94
+ withdrawInputMode(): InputModeFromAction<WithdrawActionInfo> {
95
+ return this._withdrawInputMode;
38
96
  }
39
97
 
40
98
  async getUserTVL(user: ContractAddr, blockIdentifier?: BlockIdentifier): Promise<TVLInfo> {
@@ -45,11 +103,11 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
45
103
  throw new Error("Not implemented");
46
104
  }
47
105
 
48
- async depositCall(amountInfo: ActionInfo, receiver: ContractAddr): Promise<Call[]> {
106
+ async depositCall(amountInfo: DepositActionInfo, receiver: ContractAddr): Promise<Call[]> {
49
107
  throw new Error("Not implemented");
50
108
  }
51
109
 
52
- async withdrawCall(amountInfo: ActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
110
+ async withdrawCall(amountInfo: WithdrawActionInfo, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
53
111
  throw new Error("Not implemented");
54
112
  }
55
113
 
@@ -57,7 +115,159 @@ export class BaseStrategy<TVLInfo, ActionInfo> extends CacheClass {
57
115
  throw new Error("Not implemented");
58
116
  }
59
117
 
118
+ async netAPY(
119
+ blockIdentifier?: BlockIdentifier,
120
+ sinceBlocks?: number,
121
+ timeperiod?: "24h" | "7d" | "30d" | "3m"
122
+ ): Promise<number | string | NetAPYDetails> {
123
+ throw new Error("Not implemented");
124
+ }
125
+
60
126
  async getPendingRewards(): Promise<HarvestInfo[]> {
61
127
  return [];
62
128
  }
129
+
130
+ async getUserRealizedAPY(
131
+ blockIdentifier?: BlockIdentifier,
132
+ sinceBlocks?: number
133
+ ): Promise<number> {
134
+ throw new Error("Not implemented");
135
+ }
136
+
137
+ async getUserPositionCards(_input: UserPositionCardsInput): Promise<UserPositionCard[]> {
138
+ throw new Error("Not implemented");
139
+ }
140
+
141
+ protected formatTokenAmountForCard(amount: Web3Number, tokenInfo: TokenInfo): string {
142
+ const displayDecimals = tokenInfo.displayDecimals ?? 2;
143
+ const fixed = Number(amount.toFixed(displayDecimals));
144
+ const normalized = Number.isFinite(fixed) ? fixed : 0;
145
+ return `${normalized.toLocaleString("en-US", {
146
+ maximumFractionDigits: displayDecimals,
147
+ minimumFractionDigits: 0,
148
+ })} ${tokenInfo.symbol}`;
149
+ }
150
+
151
+ protected formatPercentForCard(value: number): string {
152
+ if (!Number.isFinite(value)) return "N/A";
153
+ return `${(value * 100).toFixed(2)}%`;
154
+ }
155
+
156
+ protected resolveApyMethod(input?: UserPositionCardsInput): string | undefined {
157
+ const fromInput = input?.apyMethod?.trim();
158
+ if (fromInput) return fromInput;
159
+ const metadataApyMethod = (this as any)?.metadata?.apyMethodology;
160
+ if (typeof metadataApyMethod === "string" && metadataApyMethod.trim().length > 0) {
161
+ return metadataApyMethod;
162
+ }
163
+ return undefined;
164
+ }
165
+
166
+ protected normalizeApyDisplayValue(
167
+ apy: number | string | NetAPYDetails | null | undefined
168
+ ): string {
169
+ if (typeof apy === "number") {
170
+ return this.formatPercentForCard(apy);
171
+ }
172
+ if (typeof apy === "string") {
173
+ return apy.trim().length > 0 ? apy : "N/A";
174
+ }
175
+ if (apy && typeof apy === "object" && "net" in apy) {
176
+ return this.formatPercentForCard(Number(apy.net));
177
+ }
178
+ return "N/A";
179
+ }
180
+
181
+ protected async resolveApyValueForCard(input?: UserPositionCardsInput): Promise<string> {
182
+ if (input && input.apy !== undefined && input.apy !== null) {
183
+ return this.normalizeApyDisplayValue(input.apy);
184
+ }
185
+ try {
186
+ const netApy = await this.netAPY();
187
+ return this.normalizeApyDisplayValue(netApy as number | string | NetAPYDetails);
188
+ } catch {
189
+ return "N/A";
190
+ }
191
+ }
192
+
193
+ protected async createApyCard(input?: UserPositionCardsInput): Promise<UserPositionCard> {
194
+ const apyMethod = this.resolveApyMethod(input);
195
+ return {
196
+ title: "APY",
197
+ value: await this.resolveApyValueForCard(input),
198
+ tooltip: apyMethod,
199
+ kind: "apy",
200
+ apyMethod,
201
+ };
202
+ }
203
+
204
+ protected formatUSDForCard(value: number): string {
205
+ if (!Number.isFinite(value)) return "$0.00";
206
+ return new Intl.NumberFormat("en-US", {
207
+ style: "currency",
208
+ currency: "USD",
209
+ maximumFractionDigits: 2,
210
+ }).format(value);
211
+ }
212
+
213
+ protected getSubValueColorFromSignedNumber(value: number): UserPositionCardSubValueColor {
214
+ if (!Number.isFinite(value)) return "default";
215
+ if (value > 0) return "positive";
216
+ if (value < 0) return "negative";
217
+ return "default";
218
+ }
219
+
220
+ /**
221
+ * Calculate lifetime earnings for a user based on provided data from client
222
+ * Formula: lifetimeEarnings = currentValue + totalWithdrawals - totalDeposits
223
+ *
224
+ * @param userTVL - The user's current TVL (SingleTokenInfo with amount, usdValue, tokenInfo)
225
+ * @param investmentFlows - Array of investment flow transactions from client
226
+ * @returns Object containing lifetime earnings, current value, and total deposits/withdrawals
227
+ */
228
+ getLifetimeEarnings(
229
+ userTVL: SingleTokenInfo,
230
+ investmentFlows: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>
231
+ ): {
232
+ tokenInfo: SingleTokenInfo;
233
+ lifetimeEarnings: Web3Number;
234
+ currentValue: Web3Number;
235
+ totalDeposits: Web3Number;
236
+ totalWithdrawals: Web3Number;
237
+ } {
238
+ // Get token decimals from userTVL
239
+ const tokenDecimals = userTVL.tokenInfo.decimals;
240
+
241
+ // Initialize totals
242
+ let totalDeposits = Web3Number.fromWei("0", tokenDecimals);
243
+ let totalWithdrawals = Web3Number.fromWei("0", tokenDecimals);
244
+
245
+ // Process investment flows
246
+ for (const flow of investmentFlows) {
247
+ const amount = Web3Number.fromWei(flow.amount, tokenDecimals);
248
+
249
+ if (flow.type === 'deposit') {
250
+ totalDeposits = totalDeposits.plus(amount);
251
+ } else if (flow.type === 'withdraw' || flow.type === 'redeem') {
252
+ totalWithdrawals = totalWithdrawals.plus(amount);
253
+ }
254
+ }
255
+
256
+ // Calculate lifetime earnings: current value + withdrawals - deposits
257
+ const lifetimeEarnings = userTVL.amount
258
+ .plus(totalWithdrawals)
259
+ .minus(totalDeposits);
260
+
261
+ return {
262
+ tokenInfo: {
263
+ tokenInfo: userTVL.tokenInfo,
264
+ amount: lifetimeEarnings,
265
+ usdValue: 0, // Lifetime earnings are not converted to USD
266
+ },
267
+ lifetimeEarnings,
268
+ currentValue: userTVL.amount,
269
+ totalDeposits,
270
+ totalWithdrawals,
271
+ };
272
+ }
63
273
  }
@@ -1,10 +1,10 @@
1
1
  import { ContractAddr } from "@/dataTypes";
2
2
 
3
- export const COMMON_CONTRACTS = [{
3
+ export const MY_ACCESS_CONTROL = {
4
4
  address: ContractAddr.from("0x0636a3f51cc37f5729e4da4b1de6a8549a28f3c0d5bf3b17f150971e451ff9c2"),
5
5
  name: "Access Controller",
6
6
  sourceCodeUrl: "https://github.com/strkfarm/strkfarm-contracts/blob/main/src/components/accessControl.cairo",
7
- }];
7
+ };
8
8
 
9
9
  export const ENDPOINTS = {
10
10
  VESU_BASE: "https://proxy.api.troves.fi/vesu",