@strkfarm/sdk 1.0.58 → 1.0.60

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,7 +1,7 @@
1
1
  import { ContractAddr, Web3Number } from "@/dataTypes";
2
2
  import { BaseStrategy, SingleActionAmount, SingleTokenInfo } from "./base-strategy";
3
3
  import { PricerBase } from "@/modules/pricerBase";
4
- import { getNoRiskTags, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, VaultPosition } from "@/interfaces";
4
+ import { FAQ, getNoRiskTags, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, VaultPosition } from "@/interfaces";
5
5
  import { Call, CallData, Contract, num, uint256 } from "starknet";
6
6
  import { VesuRebalanceSettings } from "./vesu-rebalance";
7
7
  import { assert, LeafData, logger, StandardMerkleTree } from "@/utils";
@@ -12,6 +12,7 @@ import { Global } from "@/global";
12
12
  import { ERC20 } from "@/modules";
13
13
 
14
14
  export interface UniversalStrategySettings {
15
+ vaultAddress: ContractAddr,
15
16
  manager: ContractAddr,
16
17
  vaultAllocator: ContractAddr,
17
18
  redeemRequestNFT: ContractAddr,
@@ -22,6 +23,10 @@ export interface UniversalStrategySettings {
22
23
  minHealthFactor: number
23
24
  }
24
25
 
26
+ export enum AUMTypes {
27
+ FINALISED = 'finalised',
28
+ DEFISPRING = 'defispring'
29
+ }
25
30
 
26
31
  export class UniversalStrategy<
27
32
  S extends UniversalStrategySettings
@@ -141,6 +146,20 @@ export class UniversalStrategy<
141
146
  return [call1, call2];
142
147
  }
143
148
 
149
+ async withdrawCall(amountInfo: SingleActionAmount, receiver: ContractAddr, owner: ContractAddr): Promise<Call[]> {
150
+ assert(
151
+ amountInfo.tokenInfo.address.eq(this.asset().address),
152
+ "Withdraw token mismatch"
153
+ );
154
+ const shares = await this.contract.call('convert_to_shares', [uint256.bnToUint256(amountInfo.amount.toWei())]);
155
+ const call = this.contract.populate("request_redeem", [
156
+ uint256.bnToUint256(shares.toString()),
157
+ receiver.address,
158
+ owner.address
159
+ ]);
160
+ return [call];
161
+ }
162
+
144
163
  /**
145
164
  * Calculates the Total Value Locked (TVL) for a specific user.
146
165
  * @param user - Address of the user
@@ -192,14 +211,22 @@ export class UniversalStrategy<
192
211
  const debt2APY = Number(debtAsset2.borrowApr.value) / 1e18;
193
212
 
194
213
  const positions = await this.getVaultPositions();
214
+ logger.verbose(`${this.metadata.name}::netAPY: positions: ${JSON.stringify(positions)}`);
215
+ if (positions.every(p => p.amount.isZero())) {
216
+ return { net: 0, splits: [{
217
+ apy: 0, id: 'base'
218
+ }, {
219
+ apy: 0, id: 'defispring'
220
+ }]};
221
+ }
195
222
  const weights = positions.map((p, index) => p.usdValue * (index % 2 == 0 ? 1 : -1));
196
223
  const baseAPYs = [collateral1APY, debt1APY, collateral2APY, debt2APY];
197
224
  assert(positions.length == baseAPYs.length, "Positions and APYs length mismatch");
198
225
  const rewardAPYs = [Number(collateralAsset1.defiSpringSupplyApr.value) / 1e18, 0, Number(collateralAsset2.defiSpringSupplyApr.value) / 1e18, 0];
199
226
  const baseAPY = this.computeAPY(baseAPYs, weights);
200
227
  const rewardAPY = this.computeAPY(rewardAPYs, weights);
201
- const apys = [...baseAPYs, ...rewardAPYs];
202
228
  const netAPY = baseAPY + rewardAPY;
229
+ logger.verbose(`${this.metadata.name}::netAPY: net: ${netAPY}, baseAPY: ${baseAPY}, rewardAPY: ${rewardAPY}`);
203
230
  return { net: netAPY, splits: [{
204
231
  apy: baseAPY, id: 'base'
205
232
  }, {
@@ -235,6 +262,17 @@ export class UniversalStrategy<
235
262
  };
236
263
  }
237
264
 
265
+ async getUnusedBalance(): Promise<SingleTokenInfo> {
266
+ const balance = await (new ERC20(this.config)).balanceOf(this.asset().address, this.metadata.additionalInfo.vaultAllocator, this.asset().decimals);
267
+ const price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
268
+ const usdValue = Number(balance.toFixed(6)) * price.price;
269
+ return {
270
+ tokenInfo: this.asset(),
271
+ amount: balance,
272
+ usdValue
273
+ };
274
+ }
275
+
238
276
  async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: {id: string, aum: Web3Number}[]}> {
239
277
  const currentAUM: bigint = await this.contract.call('aum', []) as bigint;
240
278
  const lastReportTime = await this.contract.call('last_report_timestamp', []);
@@ -246,13 +284,30 @@ export class UniversalStrategy<
246
284
  const leg1AUM = await vesuAdapter1.getPositions(this.config);
247
285
  const leg2AUM = await vesuAdapter2.getPositions(this.config);
248
286
 
249
- const balance = await (new ERC20(this.config)).balanceOf(this.asset().address, this.metadata.additionalInfo.vaultAllocator, this.asset().decimals);
250
- logger.verbose(`${this.getTag()} unused balance: ${balance}`);
287
+ const balance = await this.getUnusedBalance();
288
+ logger.verbose(`${this.getTag()} unused balance: ${balance.amount.toNumber()}`);
251
289
 
252
- const aumToken = leg1AUM[0].amount
290
+ const vesuAum = leg1AUM[0].amount
253
291
  .plus(leg2AUM[0].usdValue / token1Price.price)
254
292
  .minus(leg1AUM[1].usdValue / token1Price.price)
255
- .minus(leg2AUM[1].amount).plus(balance);
293
+ .minus(leg2AUM[1].amount);
294
+ logger.verbose(`${this.getTag()} Vesu AUM: leg1: ${leg1AUM[0].amount.toNumber()}, ${leg1AUM[1].amount.toNumber()}, leg2: ${leg2AUM[0].amount.toNumber()}, ${leg2AUM[1].amount.toNumber()}`);
295
+
296
+ const zeroAmt = Web3Number.fromWei('0', this.asset().decimals);
297
+ const net = {
298
+ tokenInfo: this.asset(),
299
+ amount: zeroAmt,
300
+ usdValue: 0
301
+ };
302
+ const prevAum = Web3Number.fromWei(currentAUM.toString(), this.asset().decimals);
303
+ if (vesuAum.isZero()) {
304
+ return { net, splits: [{
305
+ aum: zeroAmt, id: AUMTypes.FINALISED
306
+ }, {
307
+ aum: zeroAmt, id: AUMTypes.DEFISPRING
308
+ }], prevAum};
309
+ }
310
+ const aumToken = vesuAum.plus(balance.amount);
256
311
  logger.verbose(`${this.getTag()} Actual AUM: ${aumToken}`);
257
312
 
258
313
  // calculate estimated growth from strk rewards
@@ -262,7 +317,6 @@ export class UniversalStrategy<
262
317
 
263
318
  const timeDiff = (Math.round(Date.now() / 1000) - Number(lastReportTime));
264
319
  const growthRate = timeDiff * defispringAPY / (365 * 24 * 60 * 60);
265
- const prevAum = Web3Number.fromWei(currentAUM.toString(), this.asset().decimals);
266
320
  const rewardAssets = prevAum.multipliedBy(growthRate);
267
321
  logger.verbose(`${this.getTag()} DefiSpring AUM time difference: ${timeDiff}`);
268
322
  logger.verbose(`${this.getTag()} Current AUM: ${currentAUM}`);
@@ -272,16 +326,13 @@ export class UniversalStrategy<
272
326
  const newAUM = aumToken.plus(rewardAssets);
273
327
  logger.verbose(`${this.getTag()} New AUM: ${newAUM}`);
274
328
 
275
- const net = {
276
- tokenInfo: this.asset(),
277
- amount: newAUM,
278
- usdValue: newAUM.multipliedBy(token1Price.price).toNumber()
279
- };
329
+ net.amount = newAUM;
330
+ net.usdValue = newAUM.multipliedBy(token1Price.price).toNumber();
280
331
  const splits = [{
281
- id: 'finalised',
332
+ id: AUMTypes.FINALISED,
282
333
  aum: aumToken
283
334
  }, {
284
- id: 'defispring',
335
+ id: AUMTypes.DEFISPRING,
285
336
  aum: rewardAssets
286
337
  }];
287
338
  return { net, splits, prevAum };
@@ -500,20 +551,25 @@ export class UniversalStrategy<
500
551
  logger.verbose(`${this.getTag()}:: borrow1Amount: ${borrow1Amount.toString()} ${vesuAdapter1.config.debt.symbol}`);
501
552
  logger.verbose(`${this.getTag()}:: borrow2Amount: ${borrow2Amount.toString()} ${vesuAdapter2.config.debt.symbol}`);
502
553
 
503
- const callSet1 = this.getVesuModifyPositionCalls({
554
+ let callSet1 = this.getVesuModifyPositionCalls({
504
555
  isLeg1: true,
505
556
  isDeposit: params.isDeposit,
506
557
  depositAmount: params.leg1DepositAmount.plus(borrow2Amount),
507
558
  debtAmount: borrow1Amount
508
559
  });
509
560
 
510
- const callSet2 = this.getVesuModifyPositionCalls({
561
+ let callSet2 = this.getVesuModifyPositionCalls({
511
562
  isLeg1: false,
512
563
  isDeposit: params.isDeposit,
513
564
  depositAmount: borrow1Amount,
514
565
  debtAmount: borrow2Amount
515
566
  });
516
567
 
568
+ if (!params.isDeposit) {
569
+ let temp = callSet2;
570
+ callSet2 = [...callSet1];
571
+ callSet1 = [...temp];
572
+ }
517
573
  const allActions = [...callSet1.map(i => i.manageCall), ...callSet2.map(i => i.manageCall)];
518
574
  const flashloanCalldata = CallData.compile([
519
575
  [...callSet1.map(i => i.proofs), ...callSet2.map(i => i.proofs)],
@@ -534,6 +590,21 @@ export class UniversalStrategy<
534
590
  return manageCall;
535
591
  }
536
592
 
593
+ async getBringLiquidityCall(params: {
594
+ amount: Web3Number
595
+ }) {
596
+ const manage1Info = this.getProofs<ApproveCallParams>(UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY);
597
+ const manageCall1 = manage1Info.callConstructor({
598
+ amount: params.amount
599
+ });
600
+ const manage2Info = this.getProofs<ApproveCallParams>(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY);
601
+ const manageCall2 = manage2Info.callConstructor({
602
+ amount: params.amount
603
+ });
604
+ const manageCall = this.getManageCall([UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY, UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY], [manageCall1, manageCall2]);
605
+ return manageCall;
606
+ }
607
+
537
608
  async getRebalanceCall(params: {
538
609
  isLeg1toLeg2: boolean,
539
610
  amount: Web3Number
@@ -567,12 +638,15 @@ export class UniversalStrategy<
567
638
  }
568
639
 
569
640
 
641
+ // useful to map readble names to proofs and calls
570
642
  export enum UNIVERSAL_MANAGE_IDS {
571
643
  FLASH_LOAN = 'flash_loan_init',
572
644
  VESU_LEG1 = 'vesu_leg1',
573
645
  VESU_LEG2 = 'vesu_leg2',
574
646
  APPROVE_TOKEN1 = 'approve_token1',
575
- APPROVE_TOKEN2 = 'approve_token2'
647
+ APPROVE_TOKEN2 = 'approve_token2',
648
+ APPROVE_BRING_LIQUIDITY = 'approve_bring_liquidity',
649
+ BRING_LIQUIDITY = 'bring_liquidity'
576
650
  }
577
651
 
578
652
  export enum UNIVERSAL_ADAPTERS {
@@ -586,7 +660,7 @@ function getLooperSettings(
586
660
  token2Symbol: string,
587
661
  vaultSettings: UniversalStrategySettings,
588
662
  pool1: ContractAddr,
589
- pool2: ContractAddr
663
+ pool2: ContractAddr,
590
664
  ) {
591
665
  const USDCToken = Global.getDefaultTokens().find(token => token.symbol === token1Symbol)!;
592
666
  const ETHToken = Global.getDefaultTokens().find(token => token.symbol === token2Symbol)!;
@@ -594,7 +668,9 @@ function getLooperSettings(
594
668
  const commonAdapter = new CommonAdapter({
595
669
  manager: vaultSettings.manager,
596
670
  asset: USDCToken.address,
597
- id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN
671
+ id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
672
+ vaultAddress: vaultSettings.vaultAddress,
673
+ vaultAllocator: vaultSettings.vaultAllocator,
598
674
  })
599
675
  const vesuAdapterUSDCETH = new VesuAdapter({
600
676
  poolId: pool1,
@@ -620,20 +696,28 @@ function getLooperSettings(
620
696
  id: UNIVERSAL_ADAPTERS.VESU_LEG2,
621
697
  adapter: vesuAdapterETHUSDC
622
698
  }])
699
+
700
+ // vesu looping
623
701
  vaultSettings.leafAdapters.push(commonAdapter.getFlashloanAdapter.bind(commonAdapter));
624
702
  vaultSettings.leafAdapters.push(vesuAdapterUSDCETH.getModifyPosition.bind(vesuAdapterUSDCETH));
625
703
  vaultSettings.leafAdapters.push(vesuAdapterETHUSDC.getModifyPosition.bind(vesuAdapterETHUSDC));
626
704
  vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(USDCToken.address, vesuAdapterUSDCETH.VESU_SINGLETON, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN1).bind(commonAdapter));
627
705
  vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(ETHToken.address, vesuAdapterETHUSDC.VESU_SINGLETON, UNIVERSAL_MANAGE_IDS.APPROVE_TOKEN2).bind(commonAdapter));
706
+
707
+ // to bridge liquidity back to vault (used by bring_liquidity)
708
+ vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(USDCToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));
709
+ vaultSettings.leafAdapters.push(commonAdapter.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY).bind(commonAdapter));
628
710
  return vaultSettings;
629
711
  }
630
712
 
631
713
  const _riskFactor: RiskFactor[] = [
632
714
  { type: RiskType.SMART_CONTRACT_RISK, value: 0.5, weight: 25, reason: "Audited by Zellic" },
633
- { type: RiskType.LIQUIDATION_RISK, value: 1, weight: 50, reason: "Liquidation risk is mitigated btable price feed on Starknet" }
715
+ { type: RiskType.LIQUIDATION_RISK, value: 1.5, weight: 50, reason: "Liquidation risk is mitigated by stable price feed on Starknet" },
716
+ { type: RiskType.TECHNICAL_RISK, value: 1, weight: 50, reason: "Technical failures like risk monitoring failures" }
634
717
  ];
635
718
 
636
719
  const usdcVaultSettings: UniversalStrategySettings = {
720
+ vaultAddress: ContractAddr.from('0x7e6498cf6a1bfc7e6fc89f1831865e2dacb9756def4ec4b031a9138788a3b5e'),
637
721
  manager: ContractAddr.from('0xf41a2b1f498a7f9629db0b8519259e66e964260a23d20003f3e42bb1997a07'),
638
722
  vaultAllocator: ContractAddr.from('0x228cca1005d3f2b55cbaba27cb291dacf1b9a92d1d6b1638195fbd3d0c1e3ba'),
639
723
  redeemRequestNFT: ContractAddr.from('0x906d03590010868cbf7590ad47043959d7af8e782089a605d9b22567b64fda'),
@@ -645,6 +729,7 @@ const usdcVaultSettings: UniversalStrategySettings = {
645
729
  }
646
730
 
647
731
  const wbtcVaultSettings: UniversalStrategySettings = {
732
+ vaultAddress: ContractAddr.from('0x5a4c1651b913aa2ea7afd9024911603152a19058624c3e425405370d62bf80c'),
648
733
  manager: ContractAddr.from('0xef8a664ffcfe46a6af550766d27c28937bf1b77fb4ab54d8553e92bca5ba34'),
649
734
  vaultAllocator: ContractAddr.from('0x1e01c25f0d9494570226ad28a7fa856c0640505e809c366a9fab4903320e735'),
650
735
  redeemRequestNFT: ContractAddr.from('0x4fec59a12f8424281c1e65a80b5de51b4e754625c60cddfcd00d46941ec37b2'),
@@ -655,11 +740,206 @@ const wbtcVaultSettings: UniversalStrategySettings = {
655
740
  minHealthFactor: 1.25
656
741
  }
657
742
 
743
+ const ethVaultSettings: UniversalStrategySettings = {
744
+ vaultAddress: ContractAddr.from('0x446c22d4d3f5cb52b4950ba832ba1df99464c6673a37c092b1d9622650dbd8'),
745
+ manager: ContractAddr.from('0x494888b37206616bd09d759dcda61e5118470b9aa7f58fb84f21c778a7b8f97'),
746
+ vaultAllocator: ContractAddr.from('0x4acc0ad6bea58cb578d60ff7c31f06f44369a7a9a7bbfffe4701f143e427bd'),
747
+ redeemRequestNFT: ContractAddr.from('0x2e6cd71e5060a254d4db00655e420db7bf89da7755bb0d5f922e2f00c76ac49'),
748
+ aumOracle: ContractAddr.from("0x4b747f2e75c057bed9aa2ce46fbdc2159dc684c15bd32d4f95983a6ecf39a05"),
749
+ leafAdapters: [],
750
+ adapters: [],
751
+ targetHealthFactor: 1.3,
752
+ minHealthFactor: 1.25
753
+ }
754
+
755
+ const strkVaultSettings: UniversalStrategySettings = {
756
+ vaultAddress: ContractAddr.from('0x55d012f57e58c96e0a5c7ebbe55853989d01e6538b15a95e7178aca4af05c21'),
757
+ manager: ContractAddr.from('0xcc6a5153ca56293405506eb20826a379d982cd738008ef7e808454d318fb81'),
758
+ vaultAllocator: ContractAddr.from('0xf29d2f82e896c0ed74c9eff220af34ac148e8b99846d1ace9fbb02c9191d01'),
759
+ redeemRequestNFT: ContractAddr.from('0x46902423bd632c428376b84fcee9cac5dbe016214e93a8103bcbde6e1de656b'),
760
+ aumOracle: ContractAddr.from("0x6d7dbfad4bb51715da211468389a623da00c0625f8f6efbea822ee5ac5231f4"),
761
+ leafAdapters: [],
762
+ adapters: [],
763
+ targetHealthFactor: 1.3,
764
+ minHealthFactor: 1.25
765
+ }
766
+
767
+ const usdtVaultSettings: UniversalStrategySettings = {
768
+ vaultAddress: ContractAddr.from('0x1c4933d1880c6778585e597154eaca7b428579d72f3aae425ad2e4d26c6bb3'),
769
+ manager: ContractAddr.from('0x39bb9843503799b552b7ed84b31c06e4ff10c0537edcddfbf01fe944b864029'),
770
+ vaultAllocator: ContractAddr.from('0x56437d18c43727ac971f6c7086031cad7d9d6ccb340f4f3785a74cc791c931a'),
771
+ redeemRequestNFT: ContractAddr.from('0x5af0c2a657eaa8e23ed78e855dac0c51e4f69e2cf91a18c472041a1f75bb41f'),
772
+ aumOracle: ContractAddr.from("0x7018f8040c8066a4ab929e6760ae52dd43b6a3a289172f514750a61fcc565cc"),
773
+ leafAdapters: [],
774
+ adapters: [],
775
+ targetHealthFactor: 1.3,
776
+ minHealthFactor: 1.25
777
+ }
778
+
779
+ export default function MetaVaultDescription() {
780
+ const logos: any = {
781
+ vesu: Protocols.VESU.logo,
782
+ endur: Protocols.ENDUR.logo,
783
+ extended: Protocols.EXTENDED.logo,
784
+ ekubo: "https://dummyimage.com/64x64/ffffff/000000&text=K",
785
+ };
786
+ const sources = [
787
+ { key: "vesu", name: "Vesu", status: "Live", description: "Integrated liquidity venue used for automated routing to capture the best available yield." },
788
+ { key: "endur", name: "Endur", status: "Coming soon", description: "Planned integration to tap into STRK staking–backed yields via our LST pipeline." },
789
+ { key: "extended", name: "Extended", status: "Coming soon", description: "Expanding coverage to additional money markets and vaults across the ecosystem." },
790
+ // { key: "ekubo", name: "Ekubo", status: "Coming soon", description: "Concentrated liquidity strategies targeted for optimized fee APR harvesting." },
791
+ ];
792
+
793
+ const containerStyle = {
794
+ maxWidth: "800px",
795
+ margin: "0 auto",
796
+ backgroundColor: "#111",
797
+ color: "#eee",
798
+ fontFamily: "Arial, sans-serif",
799
+ borderRadius: "12px",
800
+ };
801
+
802
+ const cardStyle = {
803
+ border: "1px solid #333",
804
+ borderRadius: "10px",
805
+ padding: "12px",
806
+ marginBottom: "12px",
807
+ backgroundColor: "#1a1a1a",
808
+ };
809
+
810
+ const logoStyle = {
811
+ width: "24px",
812
+ height: "24px",
813
+ borderRadius: "8px",
814
+ border: "1px solid #444",
815
+ backgroundColor: "#222",
816
+ };
817
+
818
+ return (
819
+ <div style={containerStyle}>
820
+ <h1 style={{ fontSize: "18px", marginBottom: "10px" }}>Meta Vault — Automated Yield Router</h1>
821
+ <p style={{ fontSize: "14px", lineHeight: "1.5", marginBottom: "16px" }}>
822
+ This Evergreen vault is a tokenized Meta Vault, auto-compounding strategy that continuously allocates your deposited
823
+ asset to the best available yield source in the ecosystem. Depositors receive vault shares that
824
+ represent a proportional claim on the underlying assets and accrued yield. Allocation shifts are
825
+ handled programmatically based on on-chain signals and risk filters, minimizing idle capital and
826
+ maximizing net APY.
827
+ </p>
828
+
829
+ <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
830
+ <p style={{ fontSize: "13px", color: "#ccc" }}>
831
+ <strong>Withdrawals:</strong> Requests can take up to <strong>1-2 hours</strong> to process as the vault unwinds and settles routing.
832
+ </p>
833
+ </div>
834
+
835
+ <h2 style={{ fontSize: "18px", marginBottom: "10px" }}>Supported Yield Sources</h2>
836
+ {sources.map((s) => (
837
+ <div key={s.key} style={cardStyle}>
838
+ <div style={{ display: "flex", gap: "10px", alignItems: "flex-start" }}>
839
+ <img src={logos[s.key]} alt={`${s.name} logo`} style={logoStyle} />
840
+ <div style={{ flex: 1 }}>
841
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
842
+ <h3 style={{ fontSize: "15px", margin: 0 }}>{s.name}</h3>
843
+ <StatusBadge status={s.status} />
844
+ </div>
845
+ {/* <p style={{ fontSize: "13px", color: "#ccc", marginTop: "4px" }}>{s.description}</p> */}
846
+ </div>
847
+ </div>
848
+ </div>
849
+ ))}
850
+ </div>
851
+ );
852
+ }
853
+
854
+ function StatusBadge({ status }: { status: string }) {
855
+ const isSoon = String(status).toLowerCase() !== "live";
856
+ const badgeStyle = {
857
+ fontSize: "12px",
858
+ padding: "2px 6px",
859
+ borderRadius: "12px",
860
+ border: "1px solid",
861
+ color: isSoon ? "rgb(196 196 195)" : "#6aff7d",
862
+ borderColor: isSoon ? "#484848" : "#6aff7d",
863
+ backgroundColor: isSoon ? "#424242" : "#002b1a",
864
+ };
865
+ return <span style={badgeStyle}>{status}</span>;
866
+ }
867
+
868
+ function getDescription(tokenSymbol: string) {
869
+ return MetaVaultDescription();
870
+ }
871
+
872
+ function getFAQs(): FAQ[] {
873
+ return [
874
+ {
875
+ question: "What is the Meta Vault?",
876
+ answer:
877
+ "The Meta Vault is a tokenized strategy that automatically allocates your deposited assets to the best available yield source in the ecosystem. It optimizes returns while minimizing idle capital.",
878
+ },
879
+ {
880
+ question: "How does yield allocation work?",
881
+ answer:
882
+ "The vault continuously monitors supported protocols and routes liquidity to the source offering the highest net yield after accounting for fees, slippage, and gas costs. Reallocations are performed automatically.",
883
+ },
884
+ {
885
+ question: "Which yield sources are supported?",
886
+ answer: (
887
+ <span>
888
+ Currently, <strong>Vesu</strong> is live. Future integrations include{" "}
889
+ <strong>Endur</strong>, <strong>Extended</strong>, and{" "}
890
+ <strong>Ekubo</strong> (all coming soon).
891
+ </span>
892
+ ),
893
+ },
894
+ {
895
+ question: "What do I receive when I deposit?",
896
+ answer:
897
+ "Depositors receive vault tokens representing their proportional share of the vault. These tokens entitle holders to both the principal and accrued yield.",
898
+ },
899
+ {
900
+ question: "How long do withdrawals take?",
901
+ answer:
902
+ "Withdrawals may take up to 1-2 hours to process, as the vault unwinds and settles liquidity routing across integrated protocols.",
903
+ },
904
+ {
905
+ question: "Is the Meta Vault non-custodial?",
906
+ answer:
907
+ "Yes. The Meta Vault operates entirely on-chain. Users always maintain control of their vault tokens, and the strategy is fully transparent.",
908
+ },
909
+ {
910
+ question: "How is risk managed?",
911
+ answer:
912
+ "Integrations are supported with active risk monitoring like liquidation risk. Only vetted protocols are included to balance yield with safety. However, usual Defi risks like smart contract risk, extreme volatile market risk, etc. are still present.",
913
+ },
914
+ {
915
+ question: "Are there any fees?",
916
+ answer:
917
+ "Troves charges a performance of 10% on the yield generated. The APY shown is net of this fee. This fee is only applied to the profits earned, ensuring that users retain their initial capital.",
918
+ },
919
+ ];
920
+ }
921
+
922
+ function getContractDetails(settings: UniversalStrategySettings): {address: ContractAddr, name: string}[] {
923
+ return [
924
+ { address: settings.vaultAddress, name: "Vault" },
925
+ { address: settings.manager, name: "Vault Manager" },
926
+ { address: settings.vaultAllocator, name: "Vault Allocator" },
927
+ { address: settings.redeemRequestNFT, name: "Redeem Request NFT" },
928
+ { address: settings.aumOracle, name: "AUM Oracle" },
929
+ ];
930
+ }
931
+
932
+ const investmentSteps: string[] = [
933
+ "Deposit funds into the vault",
934
+ "Vault manager allocates funds to optimal yield sources",
935
+ "Vault manager reports asset under management (AUM) regularly to the vault",
936
+ "Request withdrawal and vault manager processes it in 1-2 hours"
937
+ ]
658
938
  export const UniversalStrategies: IStrategyMetadata<UniversalStrategySettings>[] =
659
939
  [
660
940
  {
661
941
  name: "USDC Evergreen",
662
- description: "A universal strategy for managing USDC assets",
942
+ description: getDescription('USDC'),
663
943
  address: ContractAddr.from('0x7e6498cf6a1bfc7e6fc89f1831865e2dacb9756def4ec4b031a9138788a3b5e'),
664
944
  launchBlock: 0,
665
945
  type: 'ERC4626',
@@ -674,13 +954,13 @@ export const UniversalStrategies: IStrategyMetadata<UniversalStrategySettings>[]
674
954
  },
675
955
  protocols: [Protocols.VESU],
676
956
  maxTVL: Web3Number.fromWei(0, 6),
677
- contractDetails: [],
678
- faqs: [],
679
- investmentSteps: [],
957
+ contractDetails: getContractDetails(usdcVaultSettings),
958
+ faqs: getFAQs(),
959
+ investmentSteps: investmentSteps,
680
960
  },
681
961
  {
682
962
  name: "WBTC Evergreen",
683
- description: "A universal strategy for managing WBTC assets",
963
+ description: getDescription('WBTC'),
684
964
  address: ContractAddr.from('0x5a4c1651b913aa2ea7afd9024911603152a19058624c3e425405370d62bf80c'),
685
965
  launchBlock: 0,
686
966
  type: 'ERC4626',
@@ -695,8 +975,71 @@ export const UniversalStrategies: IStrategyMetadata<UniversalStrategySettings>[]
695
975
  },
696
976
  protocols: [Protocols.VESU],
697
977
  maxTVL: Web3Number.fromWei(0, 8),
698
- contractDetails: [],
699
- faqs: [],
700
- investmentSteps: [],
978
+ contractDetails: getContractDetails(wbtcVaultSettings),
979
+ faqs: getFAQs(),
980
+ investmentSteps: investmentSteps,
701
981
  },
982
+ {
983
+ name: "ETH Evergreen",
984
+ description: getDescription('ETH'),
985
+ address: ContractAddr.from('0x446c22d4d3f5cb52b4950ba832ba1df99464c6673a37c092b1d9622650dbd8'),
986
+ launchBlock: 0,
987
+ type: 'ERC4626',
988
+ depositTokens: [Global.getDefaultTokens().find(token => token.symbol === 'ETH')!],
989
+ additionalInfo: getLooperSettings('ETH', 'WBTC', ethVaultSettings, VesuPools.Genesis, VesuPools.Genesis),
990
+ risk: {
991
+ riskFactor: _riskFactor,
992
+ netRisk:
993
+ _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
994
+ _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
995
+ notARisks: getNoRiskTags(_riskFactor)
996
+ },
997
+ protocols: [Protocols.VESU],
998
+ maxTVL: Web3Number.fromWei(0, 18),
999
+ contractDetails: getContractDetails(ethVaultSettings),
1000
+ faqs: getFAQs(),
1001
+ investmentSteps: investmentSteps,
1002
+ },
1003
+ {
1004
+ name: "STRK Evergreen",
1005
+ description: getDescription('STRK'),
1006
+ address: ContractAddr.from('0x55d012f57e58c96e0a5c7ebbe55853989d01e6538b15a95e7178aca4af05c21'),
1007
+ launchBlock: 0,
1008
+ type: 'ERC4626',
1009
+ depositTokens: [Global.getDefaultTokens().find(token => token.symbol === 'STRK')!],
1010
+ additionalInfo: getLooperSettings('STRK', 'ETH', strkVaultSettings, VesuPools.Genesis, VesuPools.Genesis),
1011
+ risk: {
1012
+ riskFactor: _riskFactor,
1013
+ netRisk:
1014
+ _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
1015
+ _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
1016
+ notARisks: getNoRiskTags(_riskFactor)
1017
+ },
1018
+ protocols: [Protocols.VESU],
1019
+ maxTVL: Web3Number.fromWei(0, 18),
1020
+ contractDetails: getContractDetails(strkVaultSettings),
1021
+ faqs: getFAQs(),
1022
+ investmentSteps: investmentSteps,
1023
+ },
1024
+ {
1025
+ name: "USDT Evergreen",
1026
+ description: getDescription('USDT'),
1027
+ address: ContractAddr.from('0x1c4933d1880c6778585e597154eaca7b428579d72f3aae425ad2e4d26c6bb3'),
1028
+ launchBlock: 0,
1029
+ type: 'ERC4626',
1030
+ depositTokens: [Global.getDefaultTokens().find(token => token.symbol === 'USDT')!],
1031
+ additionalInfo: getLooperSettings('USDT', 'ETH', usdtVaultSettings, VesuPools.Genesis, VesuPools.Genesis),
1032
+ risk: {
1033
+ riskFactor: _riskFactor,
1034
+ netRisk:
1035
+ _riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
1036
+ _riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
1037
+ notARisks: getNoRiskTags(_riskFactor)
1038
+ },
1039
+ protocols: [Protocols.VESU],
1040
+ maxTVL: Web3Number.fromWei(0, 6),
1041
+ contractDetails: getContractDetails(usdtVaultSettings),
1042
+ faqs: getFAQs(),
1043
+ investmentSteps: investmentSteps,
1044
+ }
702
1045
  ]
@@ -1,29 +1,29 @@
1
- interface CacheData {
2
- timestamp: number;
3
- ttl: number;
4
- data: any;
5
- }
6
- export class CacheClass {
7
- readonly cache: Map<string, CacheData> = new Map();
8
-
9
- setCache(key: string, data: any, ttl: number = 60000): void {
10
- const timestamp = Date.now();
11
- this.cache.set(key, { timestamp, ttl, data });
12
- }
13
-
14
- getCache<T>(key: string): T | null {
15
- const cachedData = this.cache.get(key);
16
- if (!cachedData || !this.isCacheValid(key)) {
17
- return null;
18
- }
19
- return cachedData.data;
20
- }
21
-
22
- isCacheValid(key: string): boolean {
23
- const cachedData = this.cache.get(key);
24
- if (!cachedData) return false;
25
-
26
- const { timestamp, ttl } = cachedData;
27
- return Date.now() - timestamp <= ttl;
28
- }
1
+ interface CacheData {
2
+ timestamp: number;
3
+ ttl: number;
4
+ data: any;
5
+ }
6
+ export class CacheClass {
7
+ readonly cache: Map<string, CacheData> = new Map();
8
+
9
+ setCache(key: string, data: any, ttl: number = 60000): void {
10
+ const timestamp = Date.now();
11
+ this.cache.set(key, { timestamp, ttl, data });
12
+ }
13
+
14
+ getCache<T>(key: string): T | null {
15
+ const cachedData = this.cache.get(key);
16
+ if (!cachedData || !this.isCacheValid(key)) {
17
+ return null;
18
+ }
19
+ return cachedData.data;
20
+ }
21
+
22
+ isCacheValid(key: string): boolean {
23
+ const cachedData = this.cache.get(key);
24
+ if (!cachedData) return false;
25
+
26
+ const { timestamp, ttl } = cachedData;
27
+ return Date.now() - timestamp <= ttl;
28
+ }
29
29
  }