@strkfarm/sdk 2.0.0-staging.38 → 2.0.0-staging.39

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.
@@ -92135,15 +92135,39 @@ spurious results.`);
92135
92135
  async getPendingRewards() {
92136
92136
  return [];
92137
92137
  }
92138
- getStrategyTooltip() {
92139
- return {
92140
- holdings: "Your Holdings",
92141
- earnings: "Lifetime Earnings"
92142
- };
92143
- }
92144
92138
  async getUserRealizedAPY(blockIdentifier, sinceBlocks) {
92145
92139
  throw new Error("Not implemented");
92146
92140
  }
92141
+ async getUserPositionCards(_input) {
92142
+ throw new Error("Not implemented");
92143
+ }
92144
+ formatTokenAmountForCard(amount, tokenInfo) {
92145
+ const displayDecimals = tokenInfo.displayDecimals ?? 2;
92146
+ const fixed = Number(amount.toFixed(displayDecimals));
92147
+ const normalized = Number.isFinite(fixed) ? fixed : 0;
92148
+ return `${normalized.toLocaleString("en-US", {
92149
+ maximumFractionDigits: displayDecimals,
92150
+ minimumFractionDigits: 0
92151
+ })} ${tokenInfo.symbol}`;
92152
+ }
92153
+ formatPercentForCard(value) {
92154
+ if (!Number.isFinite(value)) return "N/A";
92155
+ return `${(value * 100).toFixed(2)}%`;
92156
+ }
92157
+ formatUSDForCard(value) {
92158
+ if (!Number.isFinite(value)) return "$0.00";
92159
+ return new Intl.NumberFormat("en-US", {
92160
+ style: "currency",
92161
+ currency: "USD",
92162
+ maximumFractionDigits: 2
92163
+ }).format(value);
92164
+ }
92165
+ getSubValueColorFromSignedNumber(value) {
92166
+ if (!Number.isFinite(value)) return "default";
92167
+ if (value > 0) return "positive";
92168
+ if (value < 0) return "negative";
92169
+ return "default";
92170
+ }
92147
92171
  /**
92148
92172
  * Calculate lifetime earnings for a user based on provided data from client
92149
92173
  * Formula: lifetimeEarnings = currentValue + totalWithdrawals - totalDeposits
@@ -103923,6 +103947,31 @@ spurious results.`);
103923
103947
  async getUserRealizedAPY(blockIdentifier = "latest", sinceBlocks = 6e5) {
103924
103948
  throw new Error("getUserRealizedAPY not implemented yet for Ekubo CL Vault strategy");
103925
103949
  }
103950
+ async getUserPositionCards(input) {
103951
+ const quoteToken = this.metadata.additionalInfo.quoteAsset;
103952
+ const [userTVL, quotePrice] = await Promise.all([
103953
+ this.getUserTVL(input.user),
103954
+ this.pricer.getPrice(quoteToken.symbol)
103955
+ ]);
103956
+ const token0IsQuote = userTVL.token0.tokenInfo.address.eq(quoteToken.address);
103957
+ const token1IsQuote = userTVL.token1.tokenInfo.address.eq(quoteToken.address);
103958
+ const token0QuoteAmount = token0IsQuote ? userTVL.token0.amount.toNumber() : userTVL.token0.usdValue / (quotePrice.price || 1);
103959
+ const token1QuoteAmount = token1IsQuote ? userTVL.token1.amount.toNumber() : userTVL.token1.usdValue / (quotePrice.price || 1);
103960
+ const totalQuoteAmount = token0QuoteAmount + token1QuoteAmount;
103961
+ const quoteAmountDisplay = Number.isFinite(totalQuoteAmount) ? totalQuoteAmount.toLocaleString("en-US", {
103962
+ maximumFractionDigits: quoteToken.displayDecimals ?? 2,
103963
+ minimumFractionDigits: 0
103964
+ }) : "0";
103965
+ return [
103966
+ {
103967
+ title: `${quoteToken.symbol} Holdings`,
103968
+ tooltip: `${quoteToken.symbol} Holdings`,
103969
+ value: `${quoteAmountDisplay} ${quoteToken.symbol}`,
103970
+ subValue: `\u2248 ${this.formatUSDForCard(userTVL.usdValue)}`,
103971
+ subValueColor: "positive"
103972
+ }
103973
+ ];
103974
+ }
103926
103975
  async feeBasedAPY(timeperiod = "24h") {
103927
103976
  const feeInfo = await this.getFeeHistory(timeperiod);
103928
103977
  const tvlNow = await this.getTVL("latest");
@@ -115663,6 +115712,9 @@ spurious results.`);
115663
115712
  async getUserRealizedAPY(blockIdentifier = "latest", sinceBlocks = 6e5) {
115664
115713
  throw new Error("getUserRealizedAPY not implemented yet for Sensei strategy");
115665
115714
  }
115715
+ async getUserPositionCards(_input) {
115716
+ return [];
115717
+ }
115666
115718
  };
115667
115719
  var senseiDescription = `Deposit your {{token1}} to automatically loop your funds via Endur ({{token2}}) and Vesu to create a delta neutral position. This strategy is designed to maximize your yield on {{token1}}. Your position is automatically adjusted periodically to maintain a healthy health factor. You receive a NFT as representation for your stake on Troves. You can withdraw anytime by redeeming your NFT for {{token1}}.`;
115668
115720
  var vesuProtocol = {
@@ -115761,7 +115813,7 @@ spurious results.`);
115761
115813
  tab: "all"
115762
115814
  }
115763
115815
  ],
115764
- liveStatus: "Active" /* ACTIVE */,
115816
+ liveStatus: "Retired" /* RETIRED */,
115765
115817
  isPaused: false,
115766
115818
  isInMaintenance: false,
115767
115819
  isAudited: false,
@@ -115818,7 +115870,18 @@ spurious results.`);
115818
115870
  multiplier: 4,
115819
115871
  logo: "https://endur.fi/favicon.ico",
115820
115872
  toolTip: "This strategy holds xSTRK. Earn 3-4x Endur points on your xSTRK due to the leverage. Points can be found on endur.fi."
115821
- }]
115873
+ }],
115874
+ discontinuationInfo: {
115875
+ info: highlightTextWithLinks(
115876
+ "This strategy is retired. All funds have been moved to Hyper xSTRK.",
115877
+ [
115878
+ {
115879
+ highlight: "Hyper xSTRK",
115880
+ link: "/strategy/hyper_xstrk"
115881
+ }
115882
+ ]
115883
+ )
115884
+ }
115822
115885
  }
115823
115886
  ];
115824
115887
 
@@ -117221,11 +117284,26 @@ spurious results.`);
117221
117284
  isReadyForNextSwap: swapAmounts[2]
117222
117285
  };
117223
117286
  }
117224
- getStrategyTooltip() {
117225
- return {
117226
- holdings: `${this.primaryToken.symbol} Left`,
117227
- earnings: `${this.secondaryToken.symbol} Accumulated`
117228
- };
117287
+ async getUserPositionCards(input) {
117288
+ const userTVL = await this.getUserTVL(input.user);
117289
+ const holdingsTitle = `${this.primaryToken.symbol} Left`;
117290
+ const earningsTitle = `${this.secondaryToken.symbol} Accumulated`;
117291
+ return [
117292
+ {
117293
+ title: holdingsTitle,
117294
+ tooltip: holdingsTitle,
117295
+ value: this.formatTokenAmountForCard(userTVL.token0.amount, userTVL.token0.tokenInfo),
117296
+ subValue: `\u2248 ${this.formatUSDForCard(userTVL.token0.usdValue)}`,
117297
+ subValueColor: "positive"
117298
+ },
117299
+ {
117300
+ title: earningsTitle,
117301
+ tooltip: earningsTitle,
117302
+ value: this.formatTokenAmountForCard(userTVL.token1.amount, userTVL.token1.tokenInfo),
117303
+ subValue: `\u2248 ${this.formatUSDForCard(userTVL.token1.usdValue)}`,
117304
+ subValueColor: "default"
117305
+ }
117306
+ ];
117229
117307
  }
117230
117308
  };
117231
117309
  var formatPriceLabel = (price) => {
@@ -120125,6 +120203,53 @@ spurious results.`);
120125
120203
  ) / 1e4;
120126
120204
  return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
120127
120205
  }
120206
+ async getUserPositionCards(input) {
120207
+ const { user, investmentFlows = [] } = input;
120208
+ const [userTVL, realizedApyRaw] = await Promise.all([
120209
+ this.getUserTVL(user),
120210
+ this.getUserRealizedAPY().catch(() => null)
120211
+ ]);
120212
+ const cards = [
120213
+ {
120214
+ title: "Your Holdings",
120215
+ tooltip: "Your Holdings",
120216
+ value: this.formatTokenAmountForCard(userTVL.amount, userTVL.tokenInfo),
120217
+ subValue: `\u2248 ${this.formatUSDForCard(userTVL.usdValue)}`,
120218
+ subValueColor: "positive"
120219
+ }
120220
+ ];
120221
+ let lifetimeAmount = userTVL.amount.multipliedBy(0);
120222
+ let lifetimeTokenInfo = userTVL.tokenInfo;
120223
+ let lifetimeUsdValue = 0;
120224
+ if (investmentFlows.length > 0) {
120225
+ try {
120226
+ const earningsResult = this.getLifetimeEarnings(userTVL, investmentFlows);
120227
+ lifetimeAmount = earningsResult.lifetimeEarnings;
120228
+ lifetimeTokenInfo = earningsResult.tokenInfo.tokenInfo;
120229
+ const userAmount = userTVL.amount.toNumber();
120230
+ if (Number.isFinite(userAmount) && userAmount > 0) {
120231
+ const pricePerToken = userTVL.usdValue / userAmount;
120232
+ lifetimeUsdValue = lifetimeAmount.toNumber() * pricePerToken;
120233
+ }
120234
+ } catch (error2) {
120235
+ logger2.warn(`${this.getTag()}::getUserPositionCards lifetime earnings fallback`, error2);
120236
+ }
120237
+ }
120238
+ cards.push({
120239
+ title: "Lifetime Earnings",
120240
+ tooltip: "Lifetime Earnings",
120241
+ value: this.formatTokenAmountForCard(lifetimeAmount, lifetimeTokenInfo),
120242
+ subValue: `\u2248 ${this.formatUSDForCard(lifetimeUsdValue)}`,
120243
+ subValueColor: this.getSubValueColorFromSignedNumber(lifetimeUsdValue)
120244
+ });
120245
+ const realizedApy = typeof realizedApyRaw === "number" ? this.formatPercentForCard(realizedApyRaw) : "N/A";
120246
+ cards.push({
120247
+ title: "Realized APY",
120248
+ tooltip: this.metadata.realizedApyMethodology || "Realized APY is based on past 14 days performance",
120249
+ value: realizedApy
120250
+ });
120251
+ return cards;
120252
+ }
120128
120253
  /**
120129
120254
  * Calculates the total TVL of the strategy.
120130
120255
  * @returns Object containing the total amount in token units and USD value
@@ -121460,6 +121585,49 @@ spurious results.`);
121460
121585
  tokenInfo: this.asset()
121461
121586
  };
121462
121587
  }
121588
+ async getUserPositionCards(input) {
121589
+ const cards = await super.getUserPositionCards(input);
121590
+ const unrealizedCardIndex = cards.findIndex((card) => card.title === "Unrealized Gains");
121591
+ try {
121592
+ const [unrealizedResult, userTVL] = await Promise.all([
121593
+ this.getUserUnrealizedGains(input.user),
121594
+ this.getUserTVL(input.user)
121595
+ ]);
121596
+ const amount = unrealizedResult.unrealizedGains;
121597
+ let usdValue = 0;
121598
+ const userAmount = userTVL.amount.toNumber();
121599
+ if (Number.isFinite(userAmount) && userAmount > 0) {
121600
+ usdValue = userTVL.usdValue / userAmount * amount.toNumber();
121601
+ }
121602
+ const unrealizedCard = {
121603
+ title: "Unrealized Gains",
121604
+ tooltip: "Unrealized gains based on current market prices vs realized prices",
121605
+ value: this.formatTokenAmountForCard(amount, unrealizedResult.tokenInfo),
121606
+ subValue: `\u2248 ${this.formatUSDForCard(usdValue)}`,
121607
+ subValueColor: this.getSubValueColorFromSignedNumber(usdValue)
121608
+ };
121609
+ if (unrealizedCardIndex === -1) {
121610
+ cards.push(unrealizedCard);
121611
+ } else {
121612
+ cards[unrealizedCardIndex] = unrealizedCard;
121613
+ }
121614
+ } catch (error2) {
121615
+ logger2.warn(`${this.getTag()}::getUserPositionCards unrealized gains fallback`, error2);
121616
+ if (unrealizedCardIndex === -1) {
121617
+ cards.push({
121618
+ title: "Unrealized Gains",
121619
+ tooltip: "Unrealized gains based on current market prices vs realized prices",
121620
+ value: this.formatTokenAmountForCard(
121621
+ Web3Number.fromWei("0", this.asset().decimals),
121622
+ this.asset()
121623
+ ),
121624
+ subValue: `\u2248 ${this.formatUSDForCard(0)}`,
121625
+ subValueColor: "default"
121626
+ });
121627
+ }
121628
+ }
121629
+ return cards;
121630
+ }
121463
121631
  //
121464
121632
  async _getMinOutputAmountLSTBuy(amountInUnderlying) {
121465
121633
  const lstTruePrice = await this.getLSTExchangeRate();
@@ -4903,15 +4903,39 @@ var BaseStrategy = class extends CacheClass {
4903
4903
  async getPendingRewards() {
4904
4904
  return [];
4905
4905
  }
4906
- getStrategyTooltip() {
4907
- return {
4908
- holdings: "Your Holdings",
4909
- earnings: "Lifetime Earnings"
4910
- };
4911
- }
4912
4906
  async getUserRealizedAPY(blockIdentifier, sinceBlocks) {
4913
4907
  throw new Error("Not implemented");
4914
4908
  }
4909
+ async getUserPositionCards(_input) {
4910
+ throw new Error("Not implemented");
4911
+ }
4912
+ formatTokenAmountForCard(amount, tokenInfo) {
4913
+ const displayDecimals = tokenInfo.displayDecimals ?? 2;
4914
+ const fixed = Number(amount.toFixed(displayDecimals));
4915
+ const normalized = Number.isFinite(fixed) ? fixed : 0;
4916
+ return `${normalized.toLocaleString("en-US", {
4917
+ maximumFractionDigits: displayDecimals,
4918
+ minimumFractionDigits: 0
4919
+ })} ${tokenInfo.symbol}`;
4920
+ }
4921
+ formatPercentForCard(value) {
4922
+ if (!Number.isFinite(value)) return "N/A";
4923
+ return `${(value * 100).toFixed(2)}%`;
4924
+ }
4925
+ formatUSDForCard(value) {
4926
+ if (!Number.isFinite(value)) return "$0.00";
4927
+ return new Intl.NumberFormat("en-US", {
4928
+ style: "currency",
4929
+ currency: "USD",
4930
+ maximumFractionDigits: 2
4931
+ }).format(value);
4932
+ }
4933
+ getSubValueColorFromSignedNumber(value) {
4934
+ if (!Number.isFinite(value)) return "default";
4935
+ if (value > 0) return "positive";
4936
+ if (value < 0) return "negative";
4937
+ return "default";
4938
+ }
4915
4939
  /**
4916
4940
  * Calculate lifetime earnings for a user based on provided data from client
4917
4941
  * Formula: lifetimeEarnings = currentValue + totalWithdrawals - totalDeposits
@@ -16704,6 +16728,31 @@ var EkuboCLVault = class _EkuboCLVault extends BaseStrategy {
16704
16728
  async getUserRealizedAPY(blockIdentifier = "latest", sinceBlocks = 6e5) {
16705
16729
  throw new Error("getUserRealizedAPY not implemented yet for Ekubo CL Vault strategy");
16706
16730
  }
16731
+ async getUserPositionCards(input) {
16732
+ const quoteToken = this.metadata.additionalInfo.quoteAsset;
16733
+ const [userTVL, quotePrice] = await Promise.all([
16734
+ this.getUserTVL(input.user),
16735
+ this.pricer.getPrice(quoteToken.symbol)
16736
+ ]);
16737
+ const token0IsQuote = userTVL.token0.tokenInfo.address.eq(quoteToken.address);
16738
+ const token1IsQuote = userTVL.token1.tokenInfo.address.eq(quoteToken.address);
16739
+ const token0QuoteAmount = token0IsQuote ? userTVL.token0.amount.toNumber() : userTVL.token0.usdValue / (quotePrice.price || 1);
16740
+ const token1QuoteAmount = token1IsQuote ? userTVL.token1.amount.toNumber() : userTVL.token1.usdValue / (quotePrice.price || 1);
16741
+ const totalQuoteAmount = token0QuoteAmount + token1QuoteAmount;
16742
+ const quoteAmountDisplay = Number.isFinite(totalQuoteAmount) ? totalQuoteAmount.toLocaleString("en-US", {
16743
+ maximumFractionDigits: quoteToken.displayDecimals ?? 2,
16744
+ minimumFractionDigits: 0
16745
+ }) : "0";
16746
+ return [
16747
+ {
16748
+ title: `${quoteToken.symbol} Holdings`,
16749
+ tooltip: `${quoteToken.symbol} Holdings`,
16750
+ value: `${quoteAmountDisplay} ${quoteToken.symbol}`,
16751
+ subValue: `\u2248 ${this.formatUSDForCard(userTVL.usdValue)}`,
16752
+ subValueColor: "positive"
16753
+ }
16754
+ ];
16755
+ }
16707
16756
  async feeBasedAPY(timeperiod = "24h") {
16708
16757
  const feeInfo = await this.getFeeHistory(timeperiod);
16709
16758
  const tvlNow = await this.getTVL("latest");
@@ -28451,6 +28500,9 @@ var SenseiVault = class _SenseiVault extends BaseStrategy {
28451
28500
  async getUserRealizedAPY(blockIdentifier = "latest", sinceBlocks = 6e5) {
28452
28501
  throw new Error("getUserRealizedAPY not implemented yet for Sensei strategy");
28453
28502
  }
28503
+ async getUserPositionCards(_input) {
28504
+ return [];
28505
+ }
28454
28506
  };
28455
28507
  var senseiDescription = `Deposit your {{token1}} to automatically loop your funds via Endur ({{token2}}) and Vesu to create a delta neutral position. This strategy is designed to maximize your yield on {{token1}}. Your position is automatically adjusted periodically to maintain a healthy health factor. You receive a NFT as representation for your stake on Troves. You can withdraw anytime by redeeming your NFT for {{token1}}.`;
28456
28508
  var vesuProtocol = {
@@ -28549,7 +28601,7 @@ var SenseiStrategies = [
28549
28601
  tab: "all"
28550
28602
  }
28551
28603
  ],
28552
- liveStatus: "Active" /* ACTIVE */,
28604
+ liveStatus: "Retired" /* RETIRED */,
28553
28605
  isPaused: false,
28554
28606
  isInMaintenance: false,
28555
28607
  isAudited: false,
@@ -28606,7 +28658,18 @@ var SenseiStrategies = [
28606
28658
  multiplier: 4,
28607
28659
  logo: "https://endur.fi/favicon.ico",
28608
28660
  toolTip: "This strategy holds xSTRK. Earn 3-4x Endur points on your xSTRK due to the leverage. Points can be found on endur.fi."
28609
- }]
28661
+ }],
28662
+ discontinuationInfo: {
28663
+ info: highlightTextWithLinks(
28664
+ "This strategy is retired. All funds have been moved to Hyper xSTRK.",
28665
+ [
28666
+ {
28667
+ highlight: "Hyper xSTRK",
28668
+ link: "/strategy/hyper_xstrk"
28669
+ }
28670
+ ]
28671
+ )
28672
+ }
28610
28673
  }
28611
28674
  ];
28612
28675
 
@@ -30013,11 +30076,26 @@ var YoLoVault = class extends BaseStrategy {
30013
30076
  isReadyForNextSwap: swapAmounts[2]
30014
30077
  };
30015
30078
  }
30016
- getStrategyTooltip() {
30017
- return {
30018
- holdings: `${this.primaryToken.symbol} Left`,
30019
- earnings: `${this.secondaryToken.symbol} Accumulated`
30020
- };
30079
+ async getUserPositionCards(input) {
30080
+ const userTVL = await this.getUserTVL(input.user);
30081
+ const holdingsTitle = `${this.primaryToken.symbol} Left`;
30082
+ const earningsTitle = `${this.secondaryToken.symbol} Accumulated`;
30083
+ return [
30084
+ {
30085
+ title: holdingsTitle,
30086
+ tooltip: holdingsTitle,
30087
+ value: this.formatTokenAmountForCard(userTVL.token0.amount, userTVL.token0.tokenInfo),
30088
+ subValue: `\u2248 ${this.formatUSDForCard(userTVL.token0.usdValue)}`,
30089
+ subValueColor: "positive"
30090
+ },
30091
+ {
30092
+ title: earningsTitle,
30093
+ tooltip: earningsTitle,
30094
+ value: this.formatTokenAmountForCard(userTVL.token1.amount, userTVL.token1.tokenInfo),
30095
+ subValue: `\u2248 ${this.formatUSDForCard(userTVL.token1.usdValue)}`,
30096
+ subValueColor: "default"
30097
+ }
30098
+ ];
30021
30099
  }
30022
30100
  };
30023
30101
  var formatPriceLabel = (price) => {
@@ -32921,6 +32999,53 @@ var UniversalStrategy = class _UniversalStrategy extends BaseStrategy {
32921
32999
  ) / 1e4;
32922
33000
  return apyForGivenBlocks * (365 * 24 * 3600) / timeDiffSeconds;
32923
33001
  }
33002
+ async getUserPositionCards(input) {
33003
+ const { user, investmentFlows = [] } = input;
33004
+ const [userTVL, realizedApyRaw] = await Promise.all([
33005
+ this.getUserTVL(user),
33006
+ this.getUserRealizedAPY().catch(() => null)
33007
+ ]);
33008
+ const cards = [
33009
+ {
33010
+ title: "Your Holdings",
33011
+ tooltip: "Your Holdings",
33012
+ value: this.formatTokenAmountForCard(userTVL.amount, userTVL.tokenInfo),
33013
+ subValue: `\u2248 ${this.formatUSDForCard(userTVL.usdValue)}`,
33014
+ subValueColor: "positive"
33015
+ }
33016
+ ];
33017
+ let lifetimeAmount = userTVL.amount.multipliedBy(0);
33018
+ let lifetimeTokenInfo = userTVL.tokenInfo;
33019
+ let lifetimeUsdValue = 0;
33020
+ if (investmentFlows.length > 0) {
33021
+ try {
33022
+ const earningsResult = this.getLifetimeEarnings(userTVL, investmentFlows);
33023
+ lifetimeAmount = earningsResult.lifetimeEarnings;
33024
+ lifetimeTokenInfo = earningsResult.tokenInfo.tokenInfo;
33025
+ const userAmount = userTVL.amount.toNumber();
33026
+ if (Number.isFinite(userAmount) && userAmount > 0) {
33027
+ const pricePerToken = userTVL.usdValue / userAmount;
33028
+ lifetimeUsdValue = lifetimeAmount.toNumber() * pricePerToken;
33029
+ }
33030
+ } catch (error) {
33031
+ logger.warn(`${this.getTag()}::getUserPositionCards lifetime earnings fallback`, error);
33032
+ }
33033
+ }
33034
+ cards.push({
33035
+ title: "Lifetime Earnings",
33036
+ tooltip: "Lifetime Earnings",
33037
+ value: this.formatTokenAmountForCard(lifetimeAmount, lifetimeTokenInfo),
33038
+ subValue: `\u2248 ${this.formatUSDForCard(lifetimeUsdValue)}`,
33039
+ subValueColor: this.getSubValueColorFromSignedNumber(lifetimeUsdValue)
33040
+ });
33041
+ const realizedApy = typeof realizedApyRaw === "number" ? this.formatPercentForCard(realizedApyRaw) : "N/A";
33042
+ cards.push({
33043
+ title: "Realized APY",
33044
+ tooltip: this.metadata.realizedApyMethodology || "Realized APY is based on past 14 days performance",
33045
+ value: realizedApy
33046
+ });
33047
+ return cards;
33048
+ }
32924
33049
  /**
32925
33050
  * Calculates the total TVL of the strategy.
32926
33051
  * @returns Object containing the total amount in token units and USD value
@@ -34259,6 +34384,49 @@ var UniversalLstMultiplierStrategy = class _UniversalLstMultiplierStrategy exten
34259
34384
  tokenInfo: this.asset()
34260
34385
  };
34261
34386
  }
34387
+ async getUserPositionCards(input) {
34388
+ const cards = await super.getUserPositionCards(input);
34389
+ const unrealizedCardIndex = cards.findIndex((card) => card.title === "Unrealized Gains");
34390
+ try {
34391
+ const [unrealizedResult, userTVL] = await Promise.all([
34392
+ this.getUserUnrealizedGains(input.user),
34393
+ this.getUserTVL(input.user)
34394
+ ]);
34395
+ const amount = unrealizedResult.unrealizedGains;
34396
+ let usdValue = 0;
34397
+ const userAmount = userTVL.amount.toNumber();
34398
+ if (Number.isFinite(userAmount) && userAmount > 0) {
34399
+ usdValue = userTVL.usdValue / userAmount * amount.toNumber();
34400
+ }
34401
+ const unrealizedCard = {
34402
+ title: "Unrealized Gains",
34403
+ tooltip: "Unrealized gains based on current market prices vs realized prices",
34404
+ value: this.formatTokenAmountForCard(amount, unrealizedResult.tokenInfo),
34405
+ subValue: `\u2248 ${this.formatUSDForCard(usdValue)}`,
34406
+ subValueColor: this.getSubValueColorFromSignedNumber(usdValue)
34407
+ };
34408
+ if (unrealizedCardIndex === -1) {
34409
+ cards.push(unrealizedCard);
34410
+ } else {
34411
+ cards[unrealizedCardIndex] = unrealizedCard;
34412
+ }
34413
+ } catch (error) {
34414
+ logger.warn(`${this.getTag()}::getUserPositionCards unrealized gains fallback`, error);
34415
+ if (unrealizedCardIndex === -1) {
34416
+ cards.push({
34417
+ title: "Unrealized Gains",
34418
+ tooltip: "Unrealized gains based on current market prices vs realized prices",
34419
+ value: this.formatTokenAmountForCard(
34420
+ Web3Number.fromWei("0", this.asset().decimals),
34421
+ this.asset()
34422
+ ),
34423
+ subValue: `\u2248 ${this.formatUSDForCard(0)}`,
34424
+ subValueColor: "default"
34425
+ });
34426
+ }
34427
+ }
34428
+ return cards;
34429
+ }
34262
34430
  //
34263
34431
  async _getMinOutputAmountLSTBuy(amountInUnderlying) {
34264
34432
  const lstTruePrice = await this.getLSTExchangeRate();
package/dist/index.d.ts CHANGED
@@ -617,6 +617,25 @@ interface NetAPYDetails {
617
617
  net: number;
618
618
  splits: NetAPYSplit[];
619
619
  }
620
+ type UserPositionCardSubValueColor = "default" | "positive" | "negative" | "info";
621
+ interface UserPositionCard {
622
+ title: string;
623
+ value: string;
624
+ tooltip?: string;
625
+ subValue?: string;
626
+ subValueColor?: UserPositionCardSubValueColor;
627
+ }
628
+ interface UserPositionCardsInput {
629
+ user: ContractAddr;
630
+ investmentFlows?: Array<{
631
+ amount: string;
632
+ type: string;
633
+ timestamp: number;
634
+ tx_hash: string;
635
+ }>;
636
+ usualTimeToEarnings?: string | null;
637
+ usualTimeToEarningsDescription?: string | null;
638
+ }
620
639
  interface CacheData {
621
640
  timestamp: number;
622
641
  ttl: number;
@@ -640,11 +659,12 @@ declare class BaseStrategy<TVLInfo, DepositActionInfo, WithdrawActionInfo = Depo
640
659
  getVaultPositions(): Promise<VaultPosition[]>;
641
660
  netAPY(blockIdentifier?: BlockIdentifier, sinceBlocks?: number, timeperiod?: "24h" | "7d" | "30d" | "3m"): Promise<number | string | NetAPYDetails>;
642
661
  getPendingRewards(): Promise<HarvestInfo[]>;
643
- getStrategyTooltip(): {
644
- holdings: string;
645
- earnings: string;
646
- };
647
662
  getUserRealizedAPY(blockIdentifier?: BlockIdentifier, sinceBlocks?: number): Promise<number>;
663
+ getUserPositionCards(_input: UserPositionCardsInput): Promise<UserPositionCard[]>;
664
+ protected formatTokenAmountForCard(amount: Web3Number, tokenInfo: TokenInfo): string;
665
+ protected formatPercentForCard(value: number): string;
666
+ protected formatUSDForCard(value: number): string;
667
+ protected getSubValueColorFromSignedNumber(value: number): UserPositionCardSubValueColor;
648
668
  /**
649
669
  * Calculate lifetime earnings for a user based on provided data from client
650
670
  * Formula: lifetimeEarnings = currentValue + totalWithdrawals - totalDeposits
@@ -974,6 +994,7 @@ declare class EkuboCLVault extends BaseStrategy<DualTokenInfo, DualActionAmount>
974
994
  * regardless of quote asset configuration.
975
995
  */
976
996
  getUserRealizedAPY(blockIdentifier?: BlockIdentifier, sinceBlocks?: number): Promise<number>;
997
+ getUserPositionCards(input: UserPositionCardsInput): Promise<UserPositionCard[]>;
977
998
  feeBasedAPY(timeperiod?: '24h' | '7d' | '30d' | '3m'): Promise<number>;
978
999
  /**
979
1000
  * Calculates assets before and now in a given token of TVL per share to observe growth
@@ -1186,6 +1207,7 @@ declare class SenseiVault extends BaseStrategy<SingleTokenInfo, SingleActionAmou
1186
1207
  * Not implemented for Sensei Strategy yet.
1187
1208
  */
1188
1209
  getUserRealizedAPY(blockIdentifier?: BlockIdentifier, sinceBlocks?: number): Promise<number>;
1210
+ getUserPositionCards(_input: UserPositionCardsInput): Promise<never[]>;
1189
1211
  }
1190
1212
  declare const SenseiStrategies: IStrategyMetadata<SenseiVaultSettings>[];
1191
1213
 
@@ -1256,10 +1278,7 @@ declare class YoLoVault extends BaseStrategy<DualTokenInfo, SingleActionAmount,
1256
1278
  isReadyForNextSwap: boolean;
1257
1279
  }>;
1258
1280
  getSettings: () => Promise<YoloSettings>;
1259
- getStrategyTooltip(): {
1260
- holdings: string;
1261
- earnings: string;
1262
- };
1281
+ getUserPositionCards(input: UserPositionCardsInput): Promise<UserPositionCard[]>;
1263
1282
  }
1264
1283
  declare const YoloVaultStrategies: IStrategyMetadata<YoloVaultSettings>[];
1265
1284
 
@@ -1728,6 +1747,7 @@ declare class UniversalStrategy<S extends UniversalStrategySettings> extends Bas
1728
1747
  * Returns the APY as a number.
1729
1748
  */
1730
1749
  getUserRealizedAPY(blockIdentifier?: BlockIdentifier, sinceBlocks?: number): Promise<number>;
1750
+ getUserPositionCards(input: UserPositionCardsInput): Promise<UserPositionCard[]>;
1731
1751
  /**
1732
1752
  * Calculates the total TVL of the strategy.
1733
1753
  * @returns Object containing the total amount in token units and USD value
@@ -1873,6 +1893,7 @@ declare class UniversalLstMultiplierStrategy extends UniversalStrategy<HyperLSTS
1873
1893
  userShare: number;
1874
1894
  tokenInfo: TokenInfo;
1875
1895
  }>;
1896
+ getUserPositionCards(input: UserPositionCardsInput): Promise<UserPositionCard[]>;
1876
1897
  private _getMinOutputAmountLSTBuy;
1877
1898
  private _getMinOutputAmountLSTSell;
1878
1899
  /**
@@ -2311,4 +2332,4 @@ declare class PasswordJsonCryptoUtil {
2311
2332
  decrypt(encryptedData: string, password: string): any;
2312
2333
  }
2313
2334
 
2314
- export { APYType, AUMTypes, AVNU_EXCHANGE, AVNU_MIDDLEWARE, type AccessControlInfo, AccessControlType, type AccountInfo, type AdapterLeafType, type AllAccountsStore, type AmountInfo, type AmountsInfo, type ApproveCallParams, AuditStatus, AutoCompounderSTRK, type AvnuSwapCallParams, AvnuWrapper, BaseAdapter, type BaseAdapterConfig, BaseStrategy, type CLVaultStrategySettings, CommonAdapter, type CommonAdapterConfig, ContractAddr, type DecreaseLeverParams, Deployer, type DualActionAmount, type DualTokenInfo, ERC20, type EkuboBounds, EkuboCLVault, EkuboCLVaultStrategies, type EkuboPoolKey, type EkuboQuote, EkuboQuoter, type EkuboRouteNode, type EkuboSplit, type FAQ, FactoryStrategyType, FatalError, type FilterOption, type FlashloanCallParams, FlowChartColors, type GenerateCallFn, Global, HyperLSTStrategies, type HyperLSTStrategySettings, type IConfig, type ICurator, type IInvestmentFlow, ILending, type ILendingMetadata, type ILendingPosition, type IProtocol, type IStrategyMetadata, type IncreaseLeverParams, Initializable, type InputModeFromAction, InstantWithdrawalVault, LSTAPRService, LSTPriceType, type LSTStats, type LeafAdapterFn, type LeafData, type LendingToken, type ManageCall, MarginType, MyNumber, type NetAPYDetails, type NetAPYSplit, Network, PRICE_ROUTER, PasswordJsonCryptoUtil, type PositionAPY, type PositionInfo, Pragma, type PriceInfo, Pricer, PricerFromApi, PricerLST, PricerRedis, Protocols, type RedemptionInfo, type RequiredFields, type RequiredKeys, type RequiredStoreConfig, type RiskFactor, RiskType, type Route, type RouteNode, SIMPLE_SANITIZER, SIMPLE_SANITIZER_V2, SIMPLE_SANITIZER_VESU_V1_DELEGATIONS, type SecurityMetadata, SenseiStrategies, SenseiVault, type SenseiVaultSettings, type SingleActionAmount, type SingleTokenInfo, type SourceCodeInfo, SourceCodeType, StandardMerkleTree, type StandardMerkleTreeData, Store, type StoreConfig, type StrategyAlert, type StrategyApyHistoryUIConfig, type StrategyCapabilities, type StrategyFilterMetadata, type StrategyInputMode, StrategyLiveStatus, type StrategyMetadata, type StrategyRegistryEntry, type StrategySettings, StrategyTag, StrategyType, type SupportedPosition, type Swap, type SwapInfo, TelegramGroupNotif, TelegramNotif, type TokenAmount, type TokenInfo, UNIVERSAL_ADAPTERS, UNIVERSAL_MANAGE_IDS, UniversalLstMultiplierStrategy, type UniversalManageCall, UniversalStrategies, UniversalStrategy, type UniversalStrategySettings, UnwrapLabsCurator, type UserYoloInfo, VESU_SINGLETON, VESU_V2_MODIFY_POSITION_SANITIZER, type VaultPosition, VaultType, VesuAdapter, type VesuAdapterConfig, type VesuAmount, VesuAmountDenomination, VesuAmountType, type VesuDefiSpringRewardsCallParams, type VesuModifyDelegationCallParams, type VesuModifyPositionCallParams, type VesuMultiplyCallParams, VesuPoolMetadata, VesuPools, VesuRebalance, type VesuRebalanceSettings, VesuRebalanceStrategies, Web3Number, YoLoVault, type YoloSettings, type YoloSpendingLevel, type YoloVaultSettings, type YoloVaultStatus, YoloVaultStrategies, ZkLend, assert, buildStrategyRegistry, createEkuboCLStrategy, createHyperLSTStrategy, createSenseiStrategy, createStrategy, createUniversalStrategy, createVesuRebalanceStrategy, createYoloVaultStrategy, detectCapabilities, extensionMap, getAPIUsingHeadlessBrowser, getAllStrategyMetadata, getAllStrategyTags, getContractDetails, getDefaultStoreConfig, getFilterMetadata, getLiveStrategies, getMainnetConfig, getNoRiskTags, getRiskColor, getRiskExplaination, getStrategiesByType, getStrategyTagDesciption, getStrategyTypeFromMetadata, getTrovesEndpoint, getVesuSingletonAddress, highlightTextWithLinks, type i257, isDualTokenStrategy, logger, toAmountsInfo, toBigInt };
2335
+ export { APYType, AUMTypes, AVNU_EXCHANGE, AVNU_MIDDLEWARE, type AccessControlInfo, AccessControlType, type AccountInfo, type AdapterLeafType, type AllAccountsStore, type AmountInfo, type AmountsInfo, type ApproveCallParams, AuditStatus, AutoCompounderSTRK, type AvnuSwapCallParams, AvnuWrapper, BaseAdapter, type BaseAdapterConfig, BaseStrategy, type CLVaultStrategySettings, CommonAdapter, type CommonAdapterConfig, ContractAddr, type DecreaseLeverParams, Deployer, type DualActionAmount, type DualTokenInfo, ERC20, type EkuboBounds, EkuboCLVault, EkuboCLVaultStrategies, type EkuboPoolKey, type EkuboQuote, EkuboQuoter, type EkuboRouteNode, type EkuboSplit, type FAQ, FactoryStrategyType, FatalError, type FilterOption, type FlashloanCallParams, FlowChartColors, type GenerateCallFn, Global, HyperLSTStrategies, type HyperLSTStrategySettings, type IConfig, type ICurator, type IInvestmentFlow, ILending, type ILendingMetadata, type ILendingPosition, type IProtocol, type IStrategyMetadata, type IncreaseLeverParams, Initializable, type InputModeFromAction, InstantWithdrawalVault, LSTAPRService, LSTPriceType, type LSTStats, type LeafAdapterFn, type LeafData, type LendingToken, type ManageCall, MarginType, MyNumber, type NetAPYDetails, type NetAPYSplit, Network, PRICE_ROUTER, PasswordJsonCryptoUtil, type PositionAPY, type PositionInfo, Pragma, type PriceInfo, Pricer, PricerFromApi, PricerLST, PricerRedis, Protocols, type RedemptionInfo, type RequiredFields, type RequiredKeys, type RequiredStoreConfig, type RiskFactor, RiskType, type Route, type RouteNode, SIMPLE_SANITIZER, SIMPLE_SANITIZER_V2, SIMPLE_SANITIZER_VESU_V1_DELEGATIONS, type SecurityMetadata, SenseiStrategies, SenseiVault, type SenseiVaultSettings, type SingleActionAmount, type SingleTokenInfo, type SourceCodeInfo, SourceCodeType, StandardMerkleTree, type StandardMerkleTreeData, Store, type StoreConfig, type StrategyAlert, type StrategyApyHistoryUIConfig, type StrategyCapabilities, type StrategyFilterMetadata, type StrategyInputMode, StrategyLiveStatus, type StrategyMetadata, type StrategyRegistryEntry, type StrategySettings, StrategyTag, StrategyType, type SupportedPosition, type Swap, type SwapInfo, TelegramGroupNotif, TelegramNotif, type TokenAmount, type TokenInfo, UNIVERSAL_ADAPTERS, UNIVERSAL_MANAGE_IDS, UniversalLstMultiplierStrategy, type UniversalManageCall, UniversalStrategies, UniversalStrategy, type UniversalStrategySettings, UnwrapLabsCurator, type UserPositionCard, type UserPositionCardSubValueColor, type UserPositionCardsInput, type UserYoloInfo, VESU_SINGLETON, VESU_V2_MODIFY_POSITION_SANITIZER, type VaultPosition, VaultType, VesuAdapter, type VesuAdapterConfig, type VesuAmount, VesuAmountDenomination, VesuAmountType, type VesuDefiSpringRewardsCallParams, type VesuModifyDelegationCallParams, type VesuModifyPositionCallParams, type VesuMultiplyCallParams, VesuPoolMetadata, VesuPools, VesuRebalance, type VesuRebalanceSettings, VesuRebalanceStrategies, Web3Number, YoLoVault, type YoloSettings, type YoloSpendingLevel, type YoloVaultSettings, type YoloVaultStatus, YoloVaultStrategies, ZkLend, assert, buildStrategyRegistry, createEkuboCLStrategy, createHyperLSTStrategy, createSenseiStrategy, createStrategy, createUniversalStrategy, createVesuRebalanceStrategy, createYoloVaultStrategy, detectCapabilities, extensionMap, getAPIUsingHeadlessBrowser, getAllStrategyMetadata, getAllStrategyTags, getContractDetails, getDefaultStoreConfig, getFilterMetadata, getLiveStrategies, getMainnetConfig, getNoRiskTags, getRiskColor, getRiskExplaination, getStrategiesByType, getStrategyTagDesciption, getStrategyTypeFromMetadata, getTrovesEndpoint, getVesuSingletonAddress, highlightTextWithLinks, type i257, isDualTokenStrategy, logger, toAmountsInfo, toBigInt };