@strkfarm/sdk 2.0.0-staging.1 → 2.0.0-staging.10

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.
@@ -10,7 +10,6 @@ import {
10
10
  IStrategyMetadata,
11
11
  RiskFactor,
12
12
  RiskType,
13
- StrategyCategory,
14
13
  StrategyTag,
15
14
  TokenInfo,
16
15
  VaultPosition,
@@ -18,6 +17,8 @@ import {
18
17
  SourceCodeType,
19
18
  AccessControlType,
20
19
  InstantWithdrawalVault,
20
+ VaultType,
21
+ StrategyLiveStatus,
21
22
  } from "@/interfaces";
22
23
  import { PricerBase } from "@/modules/pricerBase";
23
24
  import { assert } from "@/utils";
@@ -43,9 +44,10 @@ import { EkuboHarvests, HarvestInfo } from "@/modules/harvests";
43
44
  import { logger } from "@/utils/logger";
44
45
  import { COMMON_CONTRACTS } from "./constants";
45
46
  import { DepegRiskLevel, ImpermanentLossLevel, MarketRiskLevel, SmartContractRiskLevel } from "@/interfaces/risks";
46
- import { gql } from "@apollo/client";
47
+ import { from, gql } from "@apollo/client";
47
48
  import apolloClient from "@/modules/apollo-client";
48
49
  import { binarySearch } from "@/utils/math-utils";
50
+ import { Quote } from "@avnu/avnu-sdk";
49
51
 
50
52
  export interface EkuboPoolKey {
51
53
  token0: ContractAddr;
@@ -647,7 +649,8 @@ export class EkuboCLVault extends BaseStrategy<
647
649
  sinceBlocks = 600000,
648
650
  timeperiod: '24h' | '7d' | '30d' | '3m' = '24h' // temp thing for fee based APY
649
651
  ): Promise<number> {
650
- const isUSDCQouteToken = this.metadata.additionalInfo.quoteAsset.symbol === "USDC";
652
+ // ! switch to USDC later
653
+ const isUSDCQouteToken = this.metadata.additionalInfo.quoteAsset.symbol === "USDC.e" || this.metadata.additionalInfo.quoteAsset.symbol === "USDC";
651
654
  if (!isUSDCQouteToken) {
652
655
  // good for LSTs and stables
653
656
  return this.netSharesBasedTrueAPY(blockIdentifier, sinceBlocks);
@@ -1181,6 +1184,12 @@ export class EkuboCLVault extends BaseStrategy<
1181
1184
  amount1: availableAmount1.minus(y),
1182
1185
  ratio: 0,
1183
1186
  };
1187
+ } else if (ratio.eq(Infinity)) {
1188
+ return {
1189
+ amount0: availableAmount0,
1190
+ amount1: Web3Number.fromWei("0", availableAmount1.decimals),
1191
+ ratio: Infinity,
1192
+ };
1184
1193
  }
1185
1194
  return {
1186
1195
  amount0: availableAmount0.plus(x),
@@ -1230,7 +1239,12 @@ export class EkuboCLVault extends BaseStrategy<
1230
1239
  };
1231
1240
  }
1232
1241
 
1233
- async getSwapInfoToHandleUnused(considerRebalance: boolean = true, newBounds: EkuboBounds | null = null, maxIterations = 20, priceRatioPrecision = 4): Promise<SwapInfo> {
1242
+ async getSwapInfoToHandleUnused(
1243
+ considerRebalance: boolean = true,
1244
+ newBounds: EkuboBounds | null = null,
1245
+ maxIterations = 20, priceRatioPrecision = 4,
1246
+ getQuoteCallback: (tokenToSell: string, tokenToBuy: string, amountWei: string, beneficiary: string) => Promise<Quote> = this.avnu.getQuotes
1247
+ ): Promise<SwapInfo> {
1234
1248
  const poolKey = await this.getPoolKey();
1235
1249
 
1236
1250
  // fetch current unused balances of vault
@@ -1295,7 +1309,8 @@ export class EkuboCLVault extends BaseStrategy<
1295
1309
  token1Bal,
1296
1310
  ekuboBounds,
1297
1311
  maxIterations,
1298
- priceRatioPrecision
1312
+ priceRatioPrecision,
1313
+ getQuoteCallback
1299
1314
  );
1300
1315
  }
1301
1316
 
@@ -1341,6 +1356,7 @@ export class EkuboCLVault extends BaseStrategy<
1341
1356
  const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal)
1342
1357
  ? poolKey.token0
1343
1358
  : poolKey.token1;
1359
+ logger.verbose(`getSwapParams => tokenToSell: ${tokenToSell.address}, expectedAmounts: ${expectedAmounts.amount0.toString()}, bal0: ${token0Bal.toString()}`);
1344
1360
  // The other token is the one to buy
1345
1361
  const tokenToBuy =
1346
1362
  tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
@@ -1379,7 +1395,8 @@ export class EkuboCLVault extends BaseStrategy<
1379
1395
  token1Bal: Web3Number,
1380
1396
  bounds: EkuboBounds,
1381
1397
  maxIterations: number = 20,
1382
- priceRatioPrecision: number = 4
1398
+ priceRatioPrecision: number = 4,
1399
+ getQuoteCallback: (tokenToSell: string, tokenToBuy: string, amountWei: string, beneficiary: string) => Promise<Quote> = this.avnu.getQuotes
1383
1400
  ): Promise<SwapInfo> {
1384
1401
  logger.verbose(
1385
1402
  `${
@@ -1434,12 +1451,7 @@ export class EkuboCLVault extends BaseStrategy<
1434
1451
  }
1435
1452
 
1436
1453
  // Get a quote for swapping the calculated amount
1437
- const quote = await this.avnu.getQuotes(
1438
- tokenToSell.address,
1439
- tokenToBuy.address,
1440
- amountToSell.toWei(),
1441
- this.address.address
1442
- );
1454
+ const quote = await getQuoteCallback(tokenToSell.address, tokenToBuy.address, amountToSell.toWei(), this.address.address);
1443
1455
 
1444
1456
  // If all of the token is to be swapped, return the swap info directly
1445
1457
  if (remainingSellAmount.eq(0)) {
@@ -1549,8 +1561,11 @@ export class EkuboCLVault extends BaseStrategy<
1549
1561
  * @param retry - Current retry attempt number (default 0)
1550
1562
  * @param adjustmentFactor - Percentage to adjust swap amount by (default 1)
1551
1563
  * @param isToken0Deficit - Whether token0 balance needs increasing (default true)
1564
+ * @param MAX_RETRIES - Maximum number of retries (default 40)
1565
+ * @param sameErrorCount - For certain errors, we just retry with same amount again. This is the count of such retries (default { count: 0, error: null })
1566
+ * @param MAX_SAME_ERROR_COUNT - For certain errors, we just retry with same amount again. This limits such retries (default 10)
1552
1567
  * @returns Array of contract calls needed for rebalancing
1553
- * @throws Error if max retries reached without successful rebalance
1568
+ * @throws Error if max retries reached without successful rebalance or max same errors reached
1554
1569
  */
1555
1570
  async rebalanceIter(
1556
1571
  swapInfo: SwapInfo,
@@ -1560,15 +1575,23 @@ export class EkuboCLVault extends BaseStrategy<
1560
1575
  retry = 0,
1561
1576
  lowerLimit = 0n,
1562
1577
  upperLimit = 0n,
1563
- MAX_RETRIES = 40
1578
+ MAX_RETRIES = 40,
1579
+ sameErrorCount: { count: number, error: null | string } = { count: 0, error: null },
1580
+ MAX_SAME_ERROR_COUNT = 10
1564
1581
  ): Promise<Call[]> {
1565
1582
 
1566
1583
  logger.verbose(
1567
1584
  `Rebalancing ${this.metadata.name}: ` +
1568
- `retry=${retry}, lowerLimit=${lowerLimit}, upperLimit=${upperLimit}, isSellTokenToken0=${isSellTokenToken0}`
1585
+ `retry=${retry}, lowerLimit=${lowerLimit}, upperLimit=${upperLimit}, isSellTokenToken0=${isSellTokenToken0}, MAX_RETRIES=${MAX_RETRIES}, sameErrorCount=${sameErrorCount.error} (${sameErrorCount.count})`
1569
1586
  );
1570
1587
 
1588
+ if (sameErrorCount.count >= MAX_SAME_ERROR_COUNT) {
1589
+ logger.error(`Rebalance failed after ${MAX_SAME_ERROR_COUNT} same errors`);
1590
+ throw new Error(`Rebalance failed after ${MAX_SAME_ERROR_COUNT} same errors`);
1591
+ }
1592
+
1571
1593
  const fromAmount = uint256.uint256ToBN(swapInfo.token_from_amount);
1594
+ const fromTokenInfo = await Global.getTokenInfoFromAddr(ContractAddr.from(swapInfo.token_from_address));
1572
1595
  logger.verbose(
1573
1596
  `Selling ${fromAmount.toString()} of token ${swapInfo.token_from_address}`
1574
1597
  );
@@ -1588,8 +1611,8 @@ export class EkuboCLVault extends BaseStrategy<
1588
1611
  );
1589
1612
 
1590
1613
  const newSwapInfo = { ...swapInfo };
1591
- const currentAmount = Web3Number.fromWei(fromAmount.toString(), 18); // 18 is ok, as its toWei eventually anyways
1592
- logger.verbose(`Current amount: ${currentAmount.toString()}`);
1614
+ const currentAmount = Web3Number.fromWei(fromAmount.toString(), fromTokenInfo.decimals);
1615
+ logger.verbose(`Current amount: ${currentAmount.toString()}, lowerLimit: ${lowerLimit.toString()}, upperLimit: ${upperLimit.toString()}`);
1593
1616
  if (
1594
1617
  err.message.includes("invalid token0 balance") ||
1595
1618
  err.message.includes("invalid token0 amount")
@@ -1642,11 +1665,43 @@ export class EkuboCLVault extends BaseStrategy<
1642
1665
  }
1643
1666
  newSwapInfo.token_from_amount = uint256.bnToUint256(nextAmount);
1644
1667
  }
1668
+ } else if (err.message.includes("Residual tokens")) {
1669
+ logger.error("Residual tokens");
1670
+ if (sameErrorCount.error == "Residual tokens") {
1671
+ sameErrorCount.count++;
1672
+ } else {
1673
+ sameErrorCount.error = "Residual tokens";
1674
+ sameErrorCount.count = 1;
1675
+ }
1676
+ // dont do anything, just try again.
1677
+ } else if (err.message.includes("Insufficient tokens received")) {
1678
+ logger.error("Insufficient tokens received");
1679
+ if (sameErrorCount.error == "Insufficient tokens received") {
1680
+ sameErrorCount.count++;
1681
+ } else {
1682
+ sameErrorCount.error = "Insufficient tokens received";
1683
+ sameErrorCount.count = 1;
1684
+ }
1685
+ // dont do anything, just try again.
1686
+ } else if (err.message.includes("Could not reach the end of the program")) {
1687
+ logger.error("Could not reach the end of the program, may be the block is full (could be a temp/permanent gas issue)");
1688
+ if (sameErrorCount.error == "Could not reach the end of the program") {
1689
+ sameErrorCount.count++;
1690
+ } else {
1691
+ sameErrorCount.error = "Could not reach the end of the program";
1692
+ sameErrorCount.count = 1;
1693
+ }
1694
+ // just try again.
1645
1695
  } else {
1646
1696
  logger.error("Unexpected error:", err);
1647
1697
  throw err;
1648
1698
  }
1649
1699
  newSwapInfo.token_to_min_amount = uint256.bnToUint256("0");
1700
+
1701
+ // if (uint256.uint256ToBN(newSwapInfo.token_from_amount) == fromAmount && sameErrorCount.error == 'loop-stuck') {
1702
+ // logger.error("Swap amount did not change, cannot proceed");
1703
+ // sameErrorCount = { count: MAX_SAME_ERROR_COUNT, error: null };
1704
+ // }
1650
1705
  return this.rebalanceIter(
1651
1706
  newSwapInfo,
1652
1707
  acc,
@@ -1654,7 +1709,10 @@ export class EkuboCLVault extends BaseStrategy<
1654
1709
  isSellTokenToken0,
1655
1710
  retry + 1,
1656
1711
  lowerLimit,
1657
- upperLimit
1712
+ upperLimit,
1713
+ MAX_RETRIES,
1714
+ sameErrorCount,
1715
+ MAX_SAME_ERROR_COUNT
1658
1716
  );
1659
1717
  }
1660
1718
  }
@@ -2088,7 +2146,12 @@ export class EkuboCLVault extends BaseStrategy<
2088
2146
  }
2089
2147
 
2090
2148
  async getInvestmentFlows() {
2091
- const netYield = await this.netAPY();
2149
+ // for LSTs, we use 30d, else 7d for the yield calculation
2150
+ // TODO Make the block compute more dynamic
2151
+ const blocksDiff = this.metadata.additionalInfo.lstContract
2152
+ ? 600000
2153
+ : 600000 / 4;
2154
+ const netYield = await this.netAPY("latest", blocksDiff, "7d" as any);
2092
2155
  const poolKey = await this.getPoolKey();
2093
2156
 
2094
2157
  const linkedFlow: IInvestmentFlow = {
@@ -2248,6 +2311,7 @@ function getLSTFAQs(lstSymbol: string): FAQ[] {
2248
2311
  }
2249
2312
 
2250
2313
  const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
2314
+ id: "ekubo_cl_xstrkstrk",
2251
2315
  name: "Ekubo xSTRK/STRK",
2252
2316
  description: <></>,
2253
2317
  address: ContractAddr.from(
@@ -2255,6 +2319,10 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
2255
2319
  ),
2256
2320
  launchBlock: 1209881,
2257
2321
  type: "Other",
2322
+ vaultType: {
2323
+ type: VaultType.FARMING,
2324
+ description: "this is a yield farming vault"
2325
+ },
2258
2326
  // must be same order as poolKey token0 and token1
2259
2327
  depositTokens: [
2260
2328
  Global.getDefaultTokens().find((t) => t.symbol === "xSTRK")!,
@@ -2262,7 +2330,10 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
2262
2330
  ],
2263
2331
  protocols: [_protocol],
2264
2332
  auditUrl: AUDIT_URL,
2265
- maxTVL: Web3Number.fromWei("0", 18),
2333
+ curator: {
2334
+ name: "Unwrap Labs",
2335
+ logo: "https://assets.troves.fi/integrations/unwraplabs/white.png"
2336
+ },
2266
2337
  risk: {
2267
2338
  riskFactor: _lstPoolRiskFactors,
2268
2339
  netRisk:
@@ -2288,6 +2359,49 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
2288
2359
  },
2289
2360
  quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
2290
2361
  },
2362
+ settings: {
2363
+ maxTVL: Web3Number.fromWei("0", 18),
2364
+ isAudited: true,
2365
+ isPaused: false,
2366
+ liveStatus: StrategyLiveStatus.ACTIVE,
2367
+ isInstantWithdrawal: true,
2368
+ hideNetEarnings: true,
2369
+ isTransactionHistDisabled: true,
2370
+ quoteToken: Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
2371
+ alerts: [
2372
+ {
2373
+ type: "info",
2374
+ text: (
2375
+ <p>
2376
+ Depending on the current position range and price, your
2377
+ input amounts are automatically adjusted to nearest
2378
+ required amounts. If you have insufficient tokens, you
2379
+ can acquire the required tokens on{" "}
2380
+ <a
2381
+ href="https://avnu.fi"
2382
+ target="_blank"
2383
+ rel="noopener noreferrer"
2384
+ >
2385
+ Avnu
2386
+ </a>
2387
+ </p>
2388
+ ),
2389
+ tab: "deposit"
2390
+ },
2391
+ {
2392
+ type: "info",
2393
+ text: (
2394
+ <>
2395
+ Depending on the current position range and price, you
2396
+ may receive both of the tokens or one of the tokens
2397
+ depending on the price
2398
+ </>
2399
+ ),
2400
+ tab: "withdraw"
2401
+ }
2402
+ ],
2403
+ tags: [StrategyTag.AUTOMATED_LP]
2404
+ },
2291
2405
  faqs: getLSTFAQs("xSTRK"),
2292
2406
  points: [{
2293
2407
  multiplier: 1,
@@ -2296,8 +2410,7 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
2296
2410
  }],
2297
2411
  contractDetails: [],
2298
2412
  investmentSteps: [],
2299
- category: StrategyCategory.ALL,
2300
- tags: [StrategyTag.EKUBO],
2413
+ tags: [StrategyTag.AUTOMATED_LP],
2301
2414
  security: {
2302
2415
  auditStatus: AuditStatus.AUDITED,
2303
2416
  sourceCode: {
@@ -2315,284 +2428,449 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
2315
2428
  },
2316
2429
  };
2317
2430
 
2318
- const lstStrategies: IStrategyMetadata<CLVaultStrategySettings>[] = [
2319
- xSTRKSTRK,
2320
- {
2321
- ...xSTRKSTRK,
2322
- name: "Ekubo xWBTC/WBTC",
2323
- description: <></>,
2324
- address: ContractAddr.from(
2325
- "0x2ea99b4971d3c277fa4a9b4beb7d4d7d169e683393a29eef263d5d57b4380a"
2326
- ),
2327
- launchBlock: 2338309,
2328
- // must be same order as poolKey token0 and token1
2329
- depositTokens: [
2330
- Global.getDefaultTokens().find((t) => t.symbol === "WBTC")!,
2331
- Global.getDefaultTokens().find((t) => t.symbol === "xWBTC")!,
2332
- ],
2333
- additionalInfo: {
2334
- ...xSTRKSTRK.additionalInfo,
2335
- quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "WBTC")!,
2336
- lstContract: Global.getDefaultTokens().find((t) => t.symbol === "xWBTC")!.address,
2431
+ // Helper to create common LST alerts
2432
+ const getLSTAlerts = () => [
2433
+ {
2434
+ tab: "deposit" as const,
2435
+ text: (
2436
+ <>
2437
+ To acquire the LST, please visit{" "}
2438
+ <a
2439
+ href="https://app.endur.fi"
2440
+ target="_blank"
2441
+ rel="noopener noreferrer"
2442
+ >
2443
+ endur.fi
2444
+ </a>
2445
+ </>
2446
+ ),
2447
+ type: "info" as const
2337
2448
  },
2338
- faqs: getLSTFAQs("xWBTC"),
2339
- points: [],
2340
- contractDetails: [],
2341
- investmentSteps: [],
2342
- category: StrategyCategory.BTC,
2343
- tags: [StrategyTag.EKUBO],
2344
- },
2345
- {
2346
- ...xSTRKSTRK,
2347
- name: "Ekubo xtBTC/tBTC",
2348
- description: <></>,
2349
- address: ContractAddr.from(
2350
- "0x785dc3dfc4e80ef2690a99512481e3ed3a5266180adda5a47e856245d68a4af"
2351
- ),
2352
- launchBlock: 2415667,
2353
- // must be same order as poolKey token0 and token1
2354
- depositTokens: [
2355
- Global.getDefaultTokens().find((t) => t.symbol === "xtBTC")!,
2356
- Global.getDefaultTokens().find((t) => t.symbol === "tBTC")!,
2357
- ],
2358
- additionalInfo: {
2359
- ...xSTRKSTRK.additionalInfo,
2360
- quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "tBTC")!,
2361
- lstContract: Global.getDefaultTokens().find((t) => t.symbol === "xtBTC")!.address,
2449
+ {
2450
+ type: "info" as const,
2451
+ text: (
2452
+ <p>
2453
+ Depending on the current position range and price, your input
2454
+ amounts are automatically adjusted to nearest required amounts.
2455
+ If you have insufficient tokens, you can acquire the required
2456
+ tokens on{" "}
2457
+ <a
2458
+ href="https://avnu.fi"
2459
+ target="_blank"
2460
+ rel="noopener noreferrer"
2461
+ >
2462
+ Avnu
2463
+ </a>
2464
+ </p>
2465
+ ),
2466
+ tab: "deposit" as const
2362
2467
  },
2363
- faqs: getLSTFAQs("xtBTC"),
2364
- points: [],
2365
- contractDetails: [],
2366
- investmentSteps: [],
2367
- category: StrategyCategory.BTC,
2368
- tags: [StrategyTag.EKUBO],
2369
- },
2370
- {
2468
+ {
2469
+ type: "info" as const,
2470
+ text: (
2471
+ <>
2472
+ Depending on the current position range and price, you may
2473
+ receive both of the tokens or one of the tokens depending on the
2474
+ price
2475
+ </>
2476
+ ),
2477
+ tab: "withdraw" as const
2478
+ }
2479
+ ];
2480
+
2481
+ // Helper to create LST strategy settings
2482
+ const createLSTSettings = (quoteTokenSymbol: string) => ({
2483
+ ...xSTRKSTRK.settings,
2484
+ isAudited: true,
2485
+ liveStatus: StrategyLiveStatus.ACTIVE,
2486
+ isInstantWithdrawal: true,
2487
+ hideNetEarnings: true,
2488
+ isTransactionHistDisabled: true,
2489
+ quoteToken: Global.getDefaultTokens().find(
2490
+ (t) => t.symbol === quoteTokenSymbol
2491
+ )!,
2492
+ alerts: getLSTAlerts(),
2493
+ tags: [StrategyTag.AUTOMATED_LP] as StrategyTag[],
2494
+ });
2495
+
2496
+ // Helper to create an LST strategy
2497
+ const createLSTStrategy = (params: {
2498
+ id: string;
2499
+ name: string;
2500
+ address: string;
2501
+ launchBlock: number;
2502
+ depositToken0Symbol: string;
2503
+ depositToken1Symbol: string;
2504
+ quoteTokenSymbol: string;
2505
+ lstSymbol: string;
2506
+ lstContractAddress?: string;
2507
+ }): IStrategyMetadata<CLVaultStrategySettings> => ({
2371
2508
  ...xSTRKSTRK,
2372
- name: "Ekubo xsBTC/solvBTC",
2509
+ id: params.id,
2510
+ name: params.name,
2373
2511
  description: <></>,
2374
- address: ContractAddr.from(
2375
- "0x3af1c7faa7c464cf2c494e988972ad1939f1103dbfb6e47e9bf0c47e49b14ef"
2376
- ),
2377
- launchBlock: 2344809,
2378
2512
  // must be same order as poolKey token0 and token1
2379
- depositTokens: [
2380
- Global.getDefaultTokens().find((t) => t.symbol === "xsBTC")!,
2381
- Global.getDefaultTokens().find((t) => t.symbol === "solvBTC")!,
2382
- ],
2383
- additionalInfo: {
2384
- ...xSTRKSTRK.additionalInfo,
2385
- quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "solvBTC")!,
2386
- lstContract: Global.getDefaultTokens().find((t) => t.symbol === "xsBTC")!.address,
2513
+ address: ContractAddr.from(params.address),
2514
+ launchBlock: params.launchBlock,
2515
+ vaultType: {
2516
+ type: VaultType.FARMING,
2517
+ description: "this is a yield farming vault"
2387
2518
  },
2388
- faqs: getLSTFAQs("xsBTC"),
2389
- points: [],
2390
- contractDetails: [],
2391
- investmentSteps: [],
2392
- category: StrategyCategory.BTC,
2393
- tags: [StrategyTag.EKUBO],
2394
- },
2395
- {
2396
- ...xSTRKSTRK,
2397
- name: "Ekubo xLBTC/LBTC",
2398
- description: <></>,
2399
- address: ContractAddr.from(
2400
- "0x314c4653ab1aa01f5465773cb879f525d7e369a137bc3ae084761aee99a1712"
2401
- ),
2402
- launchBlock: 2412442,
2403
- // must be same order as poolKey token0 and token1
2404
2519
  depositTokens: [
2405
- Global.getDefaultTokens().find((t) => t.symbol === "LBTC")!,
2406
- Global.getDefaultTokens().find((t) => t.symbol === "xLBTC")!,
2520
+ Global.getDefaultTokens().find(
2521
+ (t) => t.symbol === params.depositToken0Symbol
2522
+ )!,
2523
+ Global.getDefaultTokens().find((t) => t.symbol === params.depositToken1Symbol)!
2407
2524
  ],
2408
2525
  additionalInfo: {
2409
- ...xSTRKSTRK.additionalInfo,
2410
- quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "LBTC")!,
2411
- lstContract: Global.getDefaultTokens().find((t) => t.symbol === "xLBTC")!.address,
2526
+ ...xSTRKSTRK.additionalInfo,
2527
+ quoteAsset: Global.getDefaultTokens().find(
2528
+ (t) => t.symbol === params.quoteTokenSymbol
2529
+ )!,
2530
+ lstContract: params.lstContractAddress
2531
+ ? ContractAddr.from(params.lstContractAddress)
2532
+ : Global.getDefaultTokens().find((t) => t.symbol === params.lstSymbol)!
2533
+ .address
2412
2534
  },
2413
- faqs: getLSTFAQs("xLBTC"),
2535
+ settings: createLSTSettings(params.quoteTokenSymbol),
2536
+ faqs: getLSTFAQs(params.lstSymbol),
2414
2537
  points: [],
2415
2538
  contractDetails: [],
2416
2539
  investmentSteps: [],
2417
- category: StrategyCategory.BTC,
2418
- tags: [StrategyTag.EKUBO],
2419
- }
2540
+ tags: params.id.toLowerCase().includes('btc') ? [StrategyTag.BTC, StrategyTag.AUTOMATED_LP] : [StrategyTag.AUTOMATED_LP]
2541
+ });
2542
+
2543
+ const lstStrategies: IStrategyMetadata<CLVaultStrategySettings>[] = [
2544
+ xSTRKSTRK,
2545
+ createLSTStrategy({
2546
+ id: "ekubo_cl_xwbtcwbtc",
2547
+ name: "Ekubo xWBTC/WBTC",
2548
+ address: "0x2ea99b4971d3c277fa4a9b4beb7d4d7d169e683393a29eef263d5d57b4380a",
2549
+ launchBlock: 2338309,
2550
+ depositToken0Symbol: "WBTC",
2551
+ depositToken1Symbol: "xWBTC",
2552
+ quoteTokenSymbol: "WBTC",
2553
+ lstSymbol: "xWBTC",
2554
+ }),
2555
+ createLSTStrategy({
2556
+ id: "ekubo_cl_xtbtctbtc",
2557
+ name: "Ekubo xtBTC/tBTC",
2558
+ address: "0x785dc3dfc4e80ef2690a99512481e3ed3a5266180adda5a47e856245d68a4af",
2559
+ launchBlock: 2415667,
2560
+ depositToken0Symbol: "xtBTC",
2561
+ depositToken1Symbol: "tBTC",
2562
+ quoteTokenSymbol: "tBTC",
2563
+ lstSymbol: "xtBTC",
2564
+ }),
2565
+ createLSTStrategy({
2566
+ id: "ekubo_cl_xsbtcsolvbtc",
2567
+ name: "Ekubo xsBTC/solvBTC",
2568
+ address: "0x3af1c7faa7c464cf2c494e988972ad1939f1103dbfb6e47e9bf0c47e49b14ef",
2569
+ launchBlock: 2344809,
2570
+ depositToken0Symbol: "xsBTC",
2571
+ depositToken1Symbol: "solvBTC",
2572
+ quoteTokenSymbol: "solvBTC",
2573
+ lstSymbol: "xsBTC",
2574
+ }),
2575
+ createLSTStrategy({
2576
+ id: "ekubo_cl_xlbtclbtc",
2577
+ name: "Ekubo xLBTC/LBTC",
2578
+ address: "0x314c4653ab1aa01f5465773cb879f525d7e369a137bc3ae084761aee99a1712",
2579
+ launchBlock: 2412442,
2580
+ depositToken0Symbol: "LBTC",
2581
+ depositToken1Symbol: "xLBTC",
2582
+ quoteTokenSymbol: "LBTC",
2583
+ lstSymbol: "xLBTC",
2584
+ })
2420
2585
  ];
2421
2586
 
2422
- const ETHUSDCRe7Strategy: IStrategyMetadata<CLVaultStrategySettings> = {
2423
- ...xSTRKSTRK,
2424
- name: "Ekubo ETH/USDC",
2425
- description: <></>,
2426
- address: ContractAddr.from(
2427
- "0x160d8fa4569ef6a12e6bf47cb943d7b5ebba8a41a69a14c1d943050ba5ff947"
2428
- ),
2429
- launchBlock: 1504232,
2430
- // must be same order as poolKey token0 and token1
2431
- depositTokens: [
2432
- Global.getDefaultTokens().find((t) => t.symbol === "ETH")!,
2433
- Global.getDefaultTokens().find((t) => t.symbol === "USDC")!
2434
- ],
2435
- apyMethodology:
2436
- "Annualized fee APY, calculated as fees earned in the last 7d divided by TVL",
2437
- additionalInfo: {
2438
- newBounds: "Managed by Re7",
2439
- truePrice: 1,
2440
- feeBps: 1000,
2441
- rebalanceConditions: {
2442
- customShouldRebalance: async (currentPrice: number) =>
2443
- currentPrice > 0.99 && currentPrice < 1.01,
2444
- minWaitHours: 6,
2445
- direction: "any"
2587
+ const getRe7Alerts = () => [
2588
+ {
2589
+ type: "info" as const,
2590
+ text: (
2591
+ <p>
2592
+ Depending on the current position range and price, your input
2593
+ amounts are automatically adjusted to nearest required amounts.
2594
+ If you have insufficient tokens, you can acquire the required
2595
+ tokens on{" "}
2596
+ <a
2597
+ href="https://avnu.fi"
2598
+ target="_blank"
2599
+ rel="noopener noreferrer"
2600
+ >
2601
+ Avnu
2602
+ </a>
2603
+ </p>
2604
+ ),
2605
+ tab: "deposit" as const
2446
2606
  },
2447
- quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "USDC")!,
2448
- },
2449
- faqs: [
2607
+ {
2608
+ type: "info" as const,
2609
+ text: (
2610
+ <>
2611
+ Depending on the current position range and price, you may
2612
+ receive both of the tokens or one of the tokens depending on the
2613
+ price
2614
+ </>
2615
+ ),
2616
+ tab: "withdraw" as const
2617
+ }
2618
+ ];
2619
+
2620
+ // Helper to create Re7 strategy settings
2621
+ const createRe7Settings = (quoteTokenSymbol: string, isBTC: boolean, isDeprecated: boolean) => ({
2622
+ ...xSTRKSTRK.settings,
2623
+ isAudited: true,
2624
+ liveStatus: isDeprecated ? StrategyLiveStatus.DEPRECATED : StrategyLiveStatus.ACTIVE,
2625
+ isInstantWithdrawal: true,
2626
+ hideNetEarnings: true,
2627
+ isTransactionHistDisabled: false,
2628
+ quoteToken: Global.getDefaultTokens().find(
2629
+ (t) => t.symbol === quoteTokenSymbol
2630
+ )!,
2631
+ alerts: getRe7Alerts(),
2632
+ tags: isBTC ? [StrategyTag.BTC, StrategyTag.AUTOMATED_LP] : [StrategyTag.AUTOMATED_LP] as StrategyTag[],
2633
+ });
2634
+
2635
+ // Helper to create Re7 FAQs
2636
+ const getRe7FAQs = () => [
2450
2637
  ...faqs,
2451
2638
  {
2452
- question: "Who is the curator of this strategy?",
2453
- answer:
2454
- <div>Re7 Labs is the curator of this strategy. Re7 Labs is a well-known Web3 asset management firm. This strategy is completely managed by them, including ownership of the vault. Troves is developer of the smart contracts and maintains infrastructure to help users access these strategies. You can find more information about them on their website <a href='https://www.re7labs.xyz' style={{textDecoration: "underline", marginLeft: "2px"}} target="_blank">here</a>.</div>
2639
+ question: "Who is the curator of this strategy?",
2640
+ answer: (
2641
+ <div>
2642
+ Re7 Labs is the curator of this strategy. Re7 Labs is a
2643
+ well-known Web3 asset management firm. This strategy is
2644
+ completely managed by them, including ownership of the vault.
2645
+ Troves is developer of the smart contracts and maintains
2646
+ infrastructure to help users access these strategies. You can
2647
+ find more information about them on their website{" "}
2648
+ <a
2649
+ href="https://www.re7labs.xyz"
2650
+ style={{
2651
+ textDecoration: "underline",
2652
+ marginLeft: "2px"
2653
+ }}
2654
+ target="_blank"
2655
+ >
2656
+ here
2657
+ </a>
2658
+ .
2659
+ </div>
2660
+ )
2455
2661
  },
2456
2662
  {
2457
- question: "How is the APY calculated?",
2458
- answer:
2459
- <div>It's an annualized fee APY, calculated as fees earned in the last 24h divided by TVL. Factors like impermanent loss are not considered.</div>
2460
- },
2461
- ],
2462
- risk: highRisk,
2463
- points: [],
2464
- curator: { name: "Re7 Labs", logo: "https://www.re7labs.xyz/favicon.ico" },
2465
- category: StrategyCategory.ALL,
2466
- tags: [StrategyTag.EKUBO],
2467
- };
2663
+ question: "How is the APY calculated?",
2664
+ answer: (
2665
+ <div>
2666
+ It's an annualized fee APY, calculated as fees earned in the
2667
+ last 24h divided by TVL. Factors like impermanent loss are not
2668
+ considered.
2669
+ </div>
2670
+ )
2671
+ }
2672
+ ];
2468
2673
 
2469
- const RE7Strategies: IStrategyMetadata<CLVaultStrategySettings>[] = [
2470
- ETHUSDCRe7Strategy,
2471
- {
2472
- ...ETHUSDCRe7Strategy,
2473
- name: "Ekubo USDC/USDT",
2674
+ // Helper to create a Re7 strategy
2675
+ const createRe7Strategy = (
2676
+ id: string,
2677
+ name: string,
2678
+ address: string,
2679
+ launchBlock: number,
2680
+ depositToken0Symbol: string,
2681
+ depositToken1Symbol: string,
2682
+ quoteTokenSymbol: string,
2683
+ risk:
2684
+ | typeof highRisk
2685
+ | typeof mediumRisk
2686
+ | { riskFactor: RiskFactor[]; netRisk: number; notARisks: RiskType[] },
2687
+ isBTC: boolean
2688
+ ): IStrategyMetadata<CLVaultStrategySettings> => ({
2689
+ ...xSTRKSTRK,
2690
+ id,
2691
+ name,
2474
2692
  description: <></>,
2475
- address: ContractAddr.from(
2476
- "0x3a4f8debaf12af97bb911099bc011d63d6c208d4c5ba8e15d7f437785b0aaa2"
2477
- ),
2478
- launchBlock: 1506139,
2479
- // must be same order as poolKey token0 and token1
2693
+ address: ContractAddr.from(address),
2694
+ launchBlock,
2695
+ vaultType: {
2696
+ type: VaultType.FARMING,
2697
+ description: "this is a yield farming vault"
2698
+ },
2480
2699
  depositTokens: [
2481
- Global.getDefaultTokens().find((t) => t.symbol === "USDC")!,
2482
- Global.getDefaultTokens().find((t) => t.symbol === "USDT")!
2700
+ Global.getDefaultTokens().find(
2701
+ (t) => t.symbol === depositToken0Symbol
2702
+ )!,
2703
+ Global.getDefaultTokens().find((t) => t.symbol === depositToken1Symbol)!
2483
2704
  ],
2484
- risk: {
2485
- riskFactor: _stableCoinPoolRiskFactors,
2486
- netRisk:
2487
- _stableCoinPoolRiskFactors.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
2488
- _stableCoinPoolRiskFactors.reduce((acc, curr) => acc + curr.weight, 0),
2489
- notARisks: getNoRiskTags(_stableCoinPoolRiskFactors),
2705
+ apyMethodology:
2706
+ "Annualized fee APY, calculated as fees earned in the last 7d divided by TVL",
2707
+ additionalInfo: {
2708
+ newBounds: "Managed by Re7",
2709
+ truePrice: 1,
2710
+ feeBps: 1000,
2711
+ rebalanceConditions: {
2712
+ customShouldRebalance: async (currentPrice: number) =>
2713
+ currentPrice > 0.99 && currentPrice < 1.01,
2714
+ minWaitHours: 6,
2715
+ direction: "any" as const
2716
+ },
2717
+ quoteAsset: Global.getDefaultTokens().find(
2718
+ (t) => t.symbol === quoteTokenSymbol
2719
+ )!
2490
2720
  },
2491
- category: StrategyCategory.ALL,
2492
- tags: [StrategyTag.EKUBO],
2493
- },
2494
- {
2495
- ...ETHUSDCRe7Strategy,
2496
- name: "Ekubo STRK/USDC",
2497
- description: <></>,
2498
- address: ContractAddr.from(
2499
- "0x351b36d0d9d8b40010658825adeeddb1397436cd41acd0ff6c6e23aaa8b5b30"
2721
+ settings: createRe7Settings(quoteTokenSymbol, isBTC, name.toLowerCase().includes('usdc.e')),
2722
+ faqs: getRe7FAQs(),
2723
+ risk,
2724
+ points: [],
2725
+ curator: { name: "Re7 Labs", logo: "https://www.re7labs.xyz/favicon.ico" },
2726
+ tags: isBTC ? [StrategyTag.BTC, StrategyTag.AUTOMATED_LP] : [StrategyTag.AUTOMATED_LP] as StrategyTag[],
2727
+ });
2728
+
2729
+ const ETHUSDCRe7Strategy = createRe7Strategy(
2730
+ "ekubo_cl_ethusdc",
2731
+ "Ekubo ETH/USDC.e",
2732
+ "0x160d8fa4569ef6a12e6bf47cb943d7b5ebba8a41a69a14c1d943050ba5ff947",
2733
+ 1504232,
2734
+ "ETH",
2735
+ "USDC.e",
2736
+ "USDC.e",
2737
+ highRisk,
2738
+ false // isBTC
2739
+ );
2740
+
2741
+ const stableCoinRisk = {
2742
+ riskFactor: _stableCoinPoolRiskFactors,
2743
+ netRisk:
2744
+ _stableCoinPoolRiskFactors.reduce(
2745
+ (acc, curr) => acc + curr.value * curr.weight,
2746
+ 0
2747
+ ) /
2748
+ _stableCoinPoolRiskFactors.reduce((acc, curr) => acc + curr.weight, 0),
2749
+ notARisks: getNoRiskTags(_stableCoinPoolRiskFactors)
2750
+ };
2751
+
2752
+ const RE7Strategies: IStrategyMetadata<CLVaultStrategySettings>[] = [
2753
+ ETHUSDCRe7Strategy,
2754
+ createRe7Strategy(
2755
+ "ekubo_cl_usdcusdt",
2756
+ "Ekubo USDC.e/USDT",
2757
+ "0x3a4f8debaf12af97bb911099bc011d63d6c208d4c5ba8e15d7f437785b0aaa2",
2758
+ 1506139,
2759
+ "USDC.e",
2760
+ "USDT",
2761
+ "USDC.e",
2762
+ stableCoinRisk,
2763
+ false // isBTC
2500
2764
  ),
2501
- launchBlock: 1504079,
2502
- // must be same order as poolKey token0 and token1
2503
- depositTokens: [
2504
- Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
2505
- Global.getDefaultTokens().find((t) => t.symbol === "USDC")!
2506
- ],
2507
- risk: highRisk,
2508
- category: StrategyCategory.ALL,
2509
- tags: [StrategyTag.EKUBO],
2510
- },
2511
- {
2512
- ...ETHUSDCRe7Strategy,
2513
- name: "Ekubo STRK/ETH",
2514
- description: <></>,
2515
- address: ContractAddr.from(
2516
- "0x4ce3024b0ee879009112d7b0e073f8a87153dd35b029347d4247ffe48d28f51"
2765
+ createRe7Strategy(
2766
+ "ekubo_cl_strkusdc",
2767
+ "Ekubo STRK/USDC.e",
2768
+ "0x351b36d0d9d8b40010658825adeeddb1397436cd41acd0ff6c6e23aaa8b5b30",
2769
+ 1504079,
2770
+ "STRK",
2771
+ "USDC.e",
2772
+ "USDC.e",
2773
+ highRisk,
2774
+ false // isBTC
2517
2775
  ),
2518
- launchBlock: 1504149,
2519
- // must be same order as poolKey token0 and token1
2520
- depositTokens: [
2521
- Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
2522
- Global.getDefaultTokens().find((t) => t.symbol === "ETH")!
2523
- ],
2524
- risk: highRisk,
2525
- category: StrategyCategory.ALL,
2526
- tags: [StrategyTag.EKUBO],
2527
- },
2528
- {
2529
- ...ETHUSDCRe7Strategy,
2530
- name: "Ekubo WBTC/USDC",
2531
- description: <></>,
2532
- address: ContractAddr.from(
2533
- "0x2bcaef2eb7706875a5fdc6853dd961a0590f850bc3a031c59887189b5e84ba1"
2776
+ createRe7Strategy(
2777
+ "ekubo_cl_strketh",
2778
+ "Ekubo STRK/ETH",
2779
+ "0x4ce3024b0ee879009112d7b0e073f8a87153dd35b029347d4247ffe48d28f51",
2780
+ 1504149,
2781
+ "STRK",
2782
+ "ETH",
2783
+ "USDC",
2784
+ highRisk,
2785
+ false // isBTC
2534
2786
  ),
2535
- launchBlock: 1506144,
2536
- // must be same order as poolKey token0 and token1
2537
- depositTokens: [
2538
- Global.getDefaultTokens().find((t) => t.symbol === "WBTC")!,
2539
- Global.getDefaultTokens().find((t) => t.symbol === "USDC")!
2540
- ],
2541
- risk: mediumRisk,
2542
- category: StrategyCategory.BTC,
2543
- tags: [StrategyTag.EKUBO],
2544
- },
2545
- {
2546
- ...ETHUSDCRe7Strategy,
2547
- name: "Ekubo tBTC/USDC",
2548
- description: <></>,
2549
- address: ContractAddr.from(
2550
- "0x4aad891a2d4432fba06b6558631bb13f6bbd7f6f33ab8c3111e344889ea4456"
2787
+ createRe7Strategy(
2788
+ "ekubo_cl_wbtcusdc",
2789
+ "Ekubo WBTC/USDC.e",
2790
+ "0x2bcaef2eb7706875a5fdc6853dd961a0590f850bc3a031c59887189b5e84ba1",
2791
+ 1506144,
2792
+ "WBTC",
2793
+ "USDC.e",
2794
+ "USDC.e",
2795
+ mediumRisk,
2796
+ true // isBTC
2551
2797
  ),
2552
- launchBlock: 1501764,
2553
- // must be same order as poolKey token0 and token1
2554
- depositTokens: [
2555
- Global.getDefaultTokens().find((t) => t.symbol === "tBTC")!,
2556
- Global.getDefaultTokens().find((t) => t.symbol === "USDC")!
2557
- ],
2558
- risk: mediumRisk,
2559
- category: StrategyCategory.BTC,
2560
- tags: [StrategyTag.EKUBO],
2561
- },
2562
- {
2563
- ...ETHUSDCRe7Strategy,
2564
- name: "Ekubo WBTC/ETH",
2565
- description: <></>,
2566
- address: ContractAddr.from(
2567
- "0x1c9232b8186d9317652f05055615f18a120c2ad9e5ee96c39e031c257fb945b"
2798
+ // createRe7Strategy(
2799
+ // "ekubo_cl_tbtcusdce",
2800
+ // "Ekubo tBTC/USDC.e",
2801
+ // "0x4aad891a2d4432fba06b6558631bb13f6bbd7f6f33ab8c3111e344889ea4456",
2802
+ // 1501764,
2803
+ // "tBTC",
2804
+ // "USDC.e",
2805
+ // "USDC.e",
2806
+ // mediumRisk,
2807
+ // ),
2808
+ createRe7Strategy(
2809
+ "ekubo_cl_wbtceth",
2810
+ "Ekubo WBTC/ETH",
2811
+ "0x1c9232b8186d9317652f05055615f18a120c2ad9e5ee96c39e031c257fb945b",
2812
+ 1506145,
2813
+ "WBTC",
2814
+ "ETH",
2815
+ "USDC",
2816
+ mediumRisk,
2817
+ true // isBTC
2568
2818
  ),
2569
- launchBlock: 1506145,
2570
- // must be same order as poolKey token0 and token1
2571
- depositTokens: [
2572
- Global.getDefaultTokens().find((t) => t.symbol === "WBTC")!,
2573
- Global.getDefaultTokens().find((t) => t.symbol === "ETH")!
2574
- ],
2575
- risk: mediumRisk,
2576
- category: StrategyCategory.BTC,
2577
- tags: [StrategyTag.EKUBO],
2578
- },
2579
- {
2580
- ...ETHUSDCRe7Strategy,
2581
- name: "Ekubo WBTC/STRK",
2582
- description: <></>,
2583
- address: ContractAddr.from(
2584
- "0x1248e385c23a929a015ec298a26560fa7745bbd6e41a886550e337b02714b1b"
2819
+ createRe7Strategy(
2820
+ "ekubo_cl_wbtcstrk",
2821
+ "Ekubo WBTC/STRK",
2822
+ "0x1248e385c23a929a015ec298a26560fa7745bbd6e41a886550e337b02714b1b",
2823
+ 1506147,
2824
+ "WBTC",
2825
+ "STRK",
2826
+ "USDC",
2827
+ highRisk,
2828
+ true // isBTC
2585
2829
  ),
2586
- launchBlock: 1506147,
2587
- // must be same order as poolKey token0 and token1
2588
- depositTokens: [
2589
- Global.getDefaultTokens().find((t) => t.symbol === "WBTC")!,
2590
- Global.getDefaultTokens().find((t) => t.symbol === "STRK")!
2591
- ],
2592
- risk: highRisk,
2593
- category: StrategyCategory.BTC,
2594
- tags: [StrategyTag.EKUBO],
2595
- }
2830
+ createRe7Strategy(
2831
+ "ekubo_cl_usdc_v2usdt",
2832
+ "Ekubo USDC/USDT",
2833
+ "0x5203a08b471e46bf33990ac83aff577bbe5a5d789e61de2c6531e3c4773d1c9",
2834
+ 3998018,
2835
+ "USDC",
2836
+ "USDT",
2837
+ "USDC",
2838
+ stableCoinRisk,
2839
+ false // isBTC
2840
+ ),
2841
+ createRe7Strategy(
2842
+ "ekubo_cl_ethusdc_v2",
2843
+ "Ekubo ETH/USDC",
2844
+ "0x4d00c7423b3c0fae3640f6099ac97acbfd8708f099e09bfe3a7a6a680399228",
2845
+ 3998025,
2846
+ "USDC",
2847
+ "ETH",
2848
+ "USDC",
2849
+ highRisk,
2850
+ false // isBTC
2851
+ ),
2852
+ createRe7Strategy(
2853
+ "ekubo_cl_strkusdc_v2",
2854
+ "Ekubo STRK/USDC",
2855
+ "0x4de22bd0a8eb4d0a18736e66dd36d20ba50bc106346bbfac3dbeaac1ab37ce1",
2856
+ 3998030,
2857
+ "USDC",
2858
+ "STRK",
2859
+ "USDC",
2860
+ highRisk,
2861
+ false // isBTC
2862
+ ),
2863
+ createRe7Strategy(
2864
+ "ekubo_cl_wbtcusdc_v2",
2865
+ "Ekubo WBTC/USDC",
2866
+ "0x76101c3b80af1103c9c6d541ca627f61b5ae7ae79d7fce96ccdf7bdb648450d",
2867
+ 3998034,
2868
+ "USDC",
2869
+ "WBTC",
2870
+ "USDC",
2871
+ mediumRisk,
2872
+ true // isBTC
2873
+ )
2596
2874
  ];
2597
2875
 
2598
2876
  /**