@strkfarm/sdk 2.0.0-dev.5 → 2.0.0-dev.50
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.
- package/dist/cli.js +190 -36
- package/dist/cli.mjs +188 -34
- package/dist/index.browser.global.js +119400 -92821
- package/dist/index.browser.mjs +13293 -11146
- package/dist/index.d.ts +2281 -1938
- package/dist/index.js +13532 -11179
- package/dist/index.mjs +14165 -11836
- package/package.json +59 -60
- package/src/data/avnu.abi.json +840 -0
- package/src/data/ekubo-price-fethcer.abi.json +265 -0
- package/src/data/redeem-request-nft.abi.json +752 -0
- package/src/data/universal-vault.abi.json +8 -7
- package/src/dataTypes/_bignumber.ts +13 -4
- package/src/dataTypes/bignumber.browser.ts +10 -1
- package/src/dataTypes/bignumber.node.ts +10 -1
- package/src/dataTypes/index.ts +3 -2
- package/src/dataTypes/mynumber.ts +141 -0
- package/src/global.ts +279 -233
- package/src/index.browser.ts +2 -1
- package/src/interfaces/common.tsx +228 -6
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +21 -12
- package/src/modules/ekubo-pricer.ts +80 -0
- package/src/modules/ekubo-quoter.ts +48 -30
- package/src/modules/erc20.ts +17 -0
- package/src/modules/harvests.ts +43 -29
- package/src/modules/index.ts +2 -1
- package/src/modules/pragma.ts +23 -8
- package/src/modules/pricer-avnu-api.ts +114 -0
- package/src/modules/pricer-from-api.ts +156 -15
- package/src/modules/pricer-lst.ts +1 -1
- package/src/modules/pricer.ts +107 -41
- package/src/modules/pricerBase.ts +2 -1
- package/src/modules/zkLend.ts +3 -2
- package/src/node/deployer.ts +36 -1
- package/src/node/pricer-redis.ts +3 -1
- package/src/strategies/base-strategy.ts +168 -16
- package/src/strategies/constants.ts +8 -3
- package/src/strategies/ekubo-cl-vault.tsx +1048 -355
- package/src/strategies/factory.ts +199 -0
- package/src/strategies/index.ts +5 -3
- package/src/strategies/registry.ts +262 -0
- package/src/strategies/sensei.ts +354 -10
- package/src/strategies/svk-strategy.ts +292 -31
- package/src/strategies/token-boosted-xstrk-carry-strategy.tsx +1261 -0
- package/src/strategies/types.ts +4 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +4 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +196 -272
- package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
- package/src/strategies/universal-adapters/common-adapter.ts +206 -203
- package/src/strategies/universal-adapters/index.ts +10 -8
- package/src/strategies/universal-adapters/svk-troves-adapter.ts +511 -0
- package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +120 -82
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +525 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +866 -860
- package/src/strategies/universal-adapters/vesu-position-common.ts +258 -0
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
- package/src/strategies/universal-lst-muliplier-strategy.tsx +895 -416
- package/src/strategies/universal-strategy.tsx +1332 -1173
- package/src/strategies/vesu-rebalance.tsx +254 -153
- package/src/strategies/yoloVault.ts +1096 -0
- package/src/utils/cacheClass.ts +11 -2
- package/src/utils/health-factor-math.ts +33 -1
- package/src/utils/index.ts +3 -1
- package/src/utils/logger.browser.ts +22 -4
- package/src/utils/logger.node.ts +259 -24
- package/src/utils/starknet-call-parser.ts +1036 -0
- package/src/utils/strategy-utils.ts +61 -0
- package/src/modules/ExtendedWrapperSDk/index.ts +0 -62
- package/src/modules/ExtendedWrapperSDk/types.ts +0 -311
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +0 -395
- package/src/strategies/universal-adapters/extended-adapter.ts +0 -662
- package/src/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +0 -34
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +0 -77
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +0 -49
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +0 -372
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +0 -1140
|
@@ -11,8 +11,16 @@ import {
|
|
|
11
11
|
Protocols,
|
|
12
12
|
RiskFactor,
|
|
13
13
|
RiskType,
|
|
14
|
+
StrategyTag,
|
|
14
15
|
TokenInfo,
|
|
15
16
|
VaultPosition,
|
|
17
|
+
AuditStatus,
|
|
18
|
+
SourceCodeType,
|
|
19
|
+
AccessControlType,
|
|
20
|
+
InstantWithdrawalVault,
|
|
21
|
+
VaultType,
|
|
22
|
+
StrategyLiveStatus,
|
|
23
|
+
UnwrapLabsCurator,
|
|
16
24
|
} from "@/interfaces";
|
|
17
25
|
import { PricerBase } from "@/modules/pricerBase";
|
|
18
26
|
import { assert } from "@/utils";
|
|
@@ -30,18 +38,25 @@ import EkuboMathAbi from "@/data/ekubo-math.abi.json";
|
|
|
30
38
|
import ERC4626Abi from "@/data/erc4626.abi.json";
|
|
31
39
|
import { Global } from "@/global";
|
|
32
40
|
import { AvnuWrapper, ERC20, SwapInfo } from "@/modules";
|
|
33
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
APYInfo,
|
|
43
|
+
BaseStrategy,
|
|
44
|
+
SingleTokenInfo,
|
|
45
|
+
UserPositionCard,
|
|
46
|
+
UserPositionCardsInput,
|
|
47
|
+
} from "./base-strategy";
|
|
34
48
|
import { DualActionAmount } from "./base-strategy";
|
|
35
49
|
import { DualTokenInfo } from "./base-strategy";
|
|
36
50
|
import { log } from "winston";
|
|
37
51
|
import { EkuboHarvests, HarvestInfo } from "@/modules/harvests";
|
|
38
52
|
import { logger } from "@/utils/logger";
|
|
39
|
-
import { COMMON_CONTRACTS } from "./constants";
|
|
40
53
|
import { DepegRiskLevel, ImpermanentLossLevel, MarketRiskLevel, SmartContractRiskLevel } from "@/interfaces/risks";
|
|
41
|
-
import { gql } from "@apollo/client";
|
|
54
|
+
import { from, gql } from "@apollo/client";
|
|
42
55
|
import apolloClient from "@/modules/apollo-client";
|
|
43
56
|
import { binarySearch } from "@/utils/math-utils";
|
|
44
57
|
import { PositionInfo } from "./universal-adapters/baseAdapter";
|
|
58
|
+
import { Quote } from "@avnu/avnu-sdk";
|
|
59
|
+
import { MY_ACCESS_CONTROL } from "./constants";
|
|
45
60
|
|
|
46
61
|
export interface EkuboPoolKey {
|
|
47
62
|
token0: ContractAddr;
|
|
@@ -118,7 +133,10 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
118
133
|
pricer: PricerBase,
|
|
119
134
|
metadata: IStrategyMetadata<CLVaultStrategySettings>
|
|
120
135
|
) {
|
|
121
|
-
super(config
|
|
136
|
+
super(config, {
|
|
137
|
+
depositInputMode: "dual",
|
|
138
|
+
withdrawInputMode: "dual"
|
|
139
|
+
});
|
|
122
140
|
this.pricer = pricer;
|
|
123
141
|
|
|
124
142
|
assert(
|
|
@@ -262,9 +280,12 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
262
280
|
async withdrawCall(
|
|
263
281
|
amountInfo: DualActionAmount,
|
|
264
282
|
receiver: ContractAddr,
|
|
265
|
-
owner: ContractAddr
|
|
283
|
+
owner: ContractAddr,
|
|
284
|
+
isMaxWithdraw: boolean = false
|
|
266
285
|
): Promise<Call[]> {
|
|
267
|
-
const shares =
|
|
286
|
+
const shares = isMaxWithdraw
|
|
287
|
+
? await this.balanceOf(receiver)
|
|
288
|
+
: await this.tokensToShares(amountInfo);
|
|
268
289
|
logger.verbose(
|
|
269
290
|
`${EkuboCLVault.name}: withdrawCall: shares=${shares.toString()}`
|
|
270
291
|
);
|
|
@@ -296,7 +317,10 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
296
317
|
return [this.contract.populate("handle_fees", [])];
|
|
297
318
|
}
|
|
298
319
|
|
|
299
|
-
async getFeeHistory(
|
|
320
|
+
async getFeeHistory(
|
|
321
|
+
timePeriod: '24h' | '7d' | '30d' | '3m' | '6m' = '24h',
|
|
322
|
+
range?: { startTimestamp?: number; endTimestamp?: number }
|
|
323
|
+
): Promise<{
|
|
300
324
|
summary: DualTokenInfo,
|
|
301
325
|
history: FeeHistory[]
|
|
302
326
|
}> {
|
|
@@ -305,8 +329,15 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
305
329
|
query ContractFeeEarnings(
|
|
306
330
|
$timeframe: String!
|
|
307
331
|
$contract: String!
|
|
332
|
+
$startTimestamp: Float
|
|
333
|
+
$endTimestamp: Float
|
|
308
334
|
) {
|
|
309
|
-
contractFeeEarnings(
|
|
335
|
+
contractFeeEarnings(
|
|
336
|
+
timeframe: $timeframe
|
|
337
|
+
contract: $contract
|
|
338
|
+
startTimestamp: $startTimestamp
|
|
339
|
+
endTimestamp: $endTimestamp
|
|
340
|
+
) {
|
|
310
341
|
contract
|
|
311
342
|
dailyEarnings {
|
|
312
343
|
date
|
|
@@ -319,7 +350,9 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
319
350
|
`,
|
|
320
351
|
variables: {
|
|
321
352
|
timeframe: timePeriod,
|
|
322
|
-
contract: this.address.address
|
|
353
|
+
contract: this.address.address,
|
|
354
|
+
startTimestamp: range?.startTimestamp,
|
|
355
|
+
endTimestamp: range?.endTimestamp
|
|
323
356
|
},
|
|
324
357
|
fetchPolicy: 'no-cache',
|
|
325
358
|
});
|
|
@@ -418,22 +451,41 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
418
451
|
const tvlBefore = await this._getTVL(blockBefore);
|
|
419
452
|
const supplyBefore = await this.totalSupply(blockBefore);
|
|
420
453
|
const priceBefore = await this.getCurrentPrice(blockBefore);
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
454
|
+
const poolKey = await this.getPoolKey(blockBefore);
|
|
455
|
+
logger.verbose(`priceBefore: ${priceBefore.price.toString()}`);
|
|
456
|
+
logger.verbose(`priceNow: ${priceNow.price.toString()}`);
|
|
457
|
+
logger.verbose(`tvlBefore: ${JSON.stringify(tvlBefore)}`);
|
|
458
|
+
logger.verbose(`tvlNow: ${JSON.stringify(tvlNow)}`);
|
|
459
|
+
|
|
460
|
+
const isQuoteTokenToken0 = this.metadata.additionalInfo.quoteAsset.address.eq(poolKey.token0);
|
|
461
|
+
logger.verbose(`isQuoteTokenToken0: ${isQuoteTokenToken0}`);
|
|
462
|
+
let tvlBeforeInBaseAsset = Web3Number.fromWei(0, this.metadata.additionalInfo.quoteAsset.decimals);
|
|
463
|
+
let tvlNowInBaseAsset = Web3Number.fromWei(0, this.metadata.additionalInfo.quoteAsset.decimals);
|
|
464
|
+
if (!isQuoteTokenToken0) {
|
|
465
|
+
tvlNowInBaseAsset = (tvlNow.amount0
|
|
466
|
+
.multipliedBy(priceNow.price))
|
|
467
|
+
.plus(tvlNow.amount1);
|
|
468
|
+
tvlBeforeInBaseAsset = (tvlBefore.amount0
|
|
469
|
+
.multipliedBy(priceBefore.price))
|
|
470
|
+
.plus(tvlBefore.amount1);
|
|
471
|
+
} else {
|
|
472
|
+
tvlNowInBaseAsset = (tvlNow.amount1
|
|
473
|
+
.multipliedBy(1 / priceNow.price))
|
|
474
|
+
.plus(tvlNow.amount0);
|
|
475
|
+
tvlBeforeInBaseAsset = (tvlBefore.amount1
|
|
476
|
+
.multipliedBy(1 / priceBefore.price))
|
|
477
|
+
.plus(tvlBefore.amount0);
|
|
478
|
+
}
|
|
479
|
+
const tvlPerShareNow = tvlNowInBaseAsset
|
|
426
480
|
.multipliedBy(1e18)
|
|
427
481
|
.dividedBy(adjustedSupplyNow.toString());
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
.plus(tvlBefore.amount1);
|
|
431
|
-
const tvlPerShareBf = tvlInToken0Bf
|
|
482
|
+
|
|
483
|
+
const tvlPerShareBf = tvlBeforeInBaseAsset
|
|
432
484
|
.multipliedBy(1e18)
|
|
433
485
|
.dividedBy(supplyBefore.toString());
|
|
434
486
|
const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
|
|
435
|
-
logger.verbose(`
|
|
436
|
-
logger.verbose(`
|
|
487
|
+
logger.verbose(`tvlNowInBaseAsset: ${tvlNowInBaseAsset.toString()}`);
|
|
488
|
+
logger.verbose(`tvlBeforeInBaseAsset: ${tvlBeforeInBaseAsset.toString()}`);
|
|
437
489
|
logger.verbose(`tvlPerShareNow: ${tvlPerShareNow.toString()}`);
|
|
438
490
|
logger.verbose(`tvlPerShareBf: ${tvlPerShareBf.toString()}`);
|
|
439
491
|
logger.verbose(`Price before: ${priceBefore.price.toString()}`);
|
|
@@ -451,6 +503,199 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
451
503
|
return (apyForGivenBlocks * (365 * 24 * 3600)) / timeDiffSeconds;
|
|
452
504
|
}
|
|
453
505
|
|
|
506
|
+
/**
|
|
507
|
+
* Calculate lifetime earnings for a user
|
|
508
|
+
* Not yet implemented for Ekubo CL Vault strategy
|
|
509
|
+
*/
|
|
510
|
+
getLifetimeEarnings(
|
|
511
|
+
userTVL: SingleTokenInfo,
|
|
512
|
+
investmentFlows: Array<{ amount: string; type: string; timestamp: number; tx_hash: string }>
|
|
513
|
+
): any {
|
|
514
|
+
throw new Error("getLifetimeEarnings is not implemented yet for this strategy");
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Calculates realized APY based on TVL per share growth, always valued in USDC.
|
|
519
|
+
* This is a vault-level metric (same for all users) and works for all strategies,
|
|
520
|
+
* regardless of quote asset configuration.
|
|
521
|
+
*/
|
|
522
|
+
async getUserRealizedAPY(
|
|
523
|
+
blockIdentifier: BlockIdentifier = "latest",
|
|
524
|
+
sinceBlocks = 600000
|
|
525
|
+
): Promise<number> {
|
|
526
|
+
throw new Error("getUserRealizedAPY not implemented yet for Ekubo CL Vault strategy");
|
|
527
|
+
|
|
528
|
+
/*
|
|
529
|
+
logger.verbose(
|
|
530
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => starting with blockIdentifier=${blockIdentifier}, sinceBlocks=${sinceBlocks}`
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
// TVL amounts (in token units) at current reference block
|
|
534
|
+
const tvlNow = await this._getTVL(blockIdentifier);
|
|
535
|
+
const supplyNow = await this.totalSupply(blockIdentifier);
|
|
536
|
+
|
|
537
|
+
// Determine current block number and timestamp
|
|
538
|
+
let blockNow =
|
|
539
|
+
typeof blockIdentifier == "number"
|
|
540
|
+
? blockIdentifier
|
|
541
|
+
: (await this.config.provider.getBlockLatestAccepted()).block_number;
|
|
542
|
+
const blockNowTime =
|
|
543
|
+
typeof blockIdentifier == "number"
|
|
544
|
+
? (await this.config.provider.getBlockWithTxs(blockIdentifier))
|
|
545
|
+
.timestamp
|
|
546
|
+
: new Date().getTime() / 1000;
|
|
547
|
+
|
|
548
|
+
// Look back window, but never before launch block
|
|
549
|
+
const blockBefore = Math.max(
|
|
550
|
+
Number(blockNow) - sinceBlocks,
|
|
551
|
+
this.metadata.launchBlock
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
// Adjust current supply by subtracting harvest reward shares (same idea as netSharesBasedTrueAPY)
|
|
555
|
+
const adjustedSupplyNow = supplyNow.minus(
|
|
556
|
+
await this.getHarvestRewardShares(blockBefore, Number(blockNow))
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
// Historical block info and TVL
|
|
560
|
+
const blockBeforeInfo = await this.config.provider.getBlockWithTxs(
|
|
561
|
+
blockBefore
|
|
562
|
+
);
|
|
563
|
+
const tvlBefore = await this._getTVL(blockBefore);
|
|
564
|
+
const supplyBefore = await this.totalSupply(blockBefore);
|
|
565
|
+
|
|
566
|
+
// Always value TVL in USDC using the pricer for both tokens at both blocks
|
|
567
|
+
const token0Info = this.metadata.depositTokens[0];
|
|
568
|
+
const token1Info = this.metadata.depositTokens[1];
|
|
569
|
+
|
|
570
|
+
const P0Now = await this.pricer.getPrice(
|
|
571
|
+
token0Info.symbol,
|
|
572
|
+
Number(blockNow)
|
|
573
|
+
);
|
|
574
|
+
const P1Now = await this.pricer.getPrice(
|
|
575
|
+
token1Info.symbol,
|
|
576
|
+
Number(blockNow)
|
|
577
|
+
);
|
|
578
|
+
const P0Before = await this.pricer.getPrice(
|
|
579
|
+
token0Info.symbol,
|
|
580
|
+
blockBefore
|
|
581
|
+
);
|
|
582
|
+
const P1Before = await this.pricer.getPrice(
|
|
583
|
+
token1Info.symbol,
|
|
584
|
+
blockBefore
|
|
585
|
+
);
|
|
586
|
+
|
|
587
|
+
// Convert token balances to USDC TVL using current and historical prices
|
|
588
|
+
const tvlNowUsdNumber =
|
|
589
|
+
Number(tvlNow.amount0.toFixed(13)) * P0Now.price +
|
|
590
|
+
Number(tvlNow.amount1.toFixed(13)) * P1Now.price;
|
|
591
|
+
const tvlBeforeUsdNumber =
|
|
592
|
+
Number(tvlBefore.amount0.toFixed(13)) * P0Before.price +
|
|
593
|
+
Number(tvlBefore.amount1.toFixed(13)) * P1Before.price;
|
|
594
|
+
|
|
595
|
+
// Represent USDC TVL as Web3Number with 6 decimals (USDC standard)
|
|
596
|
+
const tvlNowInUSDC = new Web3Number(
|
|
597
|
+
tvlNowUsdNumber.toFixed(13),
|
|
598
|
+
6
|
|
599
|
+
);
|
|
600
|
+
const tvlBeforeInUSDC = new Web3Number(
|
|
601
|
+
tvlBeforeUsdNumber.toFixed(13),
|
|
602
|
+
6
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
const tvlPerShareNow = tvlNowInUSDC
|
|
606
|
+
.multipliedBy(1e18)
|
|
607
|
+
.dividedBy(adjustedSupplyNow.toString());
|
|
608
|
+
|
|
609
|
+
const tvlPerShareBf = tvlBeforeInUSDC
|
|
610
|
+
.multipliedBy(1e18)
|
|
611
|
+
.dividedBy(supplyBefore.toString());
|
|
612
|
+
|
|
613
|
+
const timeDiffSeconds = blockNowTime - blockBeforeInfo.timestamp;
|
|
614
|
+
|
|
615
|
+
logger.verbose(
|
|
616
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => token0=${token0Info.symbol}, token1=${token1Info.symbol}`
|
|
617
|
+
);
|
|
618
|
+
logger.verbose(
|
|
619
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => P0Now=${P0Now.price}, P1Now=${P1Now.price}, P0Before=${P0Before.price}, P1Before=${P1Before.price}`
|
|
620
|
+
);
|
|
621
|
+
logger.verbose(
|
|
622
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => raw tvlNow amounts: token0=${tvlNow.amount0.toString()}, token1=${tvlNow.amount1.toString()}`
|
|
623
|
+
);
|
|
624
|
+
logger.verbose(
|
|
625
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => raw tvlBefore amounts: token0=${tvlBefore.amount0.toString()}, token1=${tvlBefore.amount1.toString()}`
|
|
626
|
+
);
|
|
627
|
+
logger.verbose(
|
|
628
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => tvlNowUsdNumber=${tvlNowUsdNumber}, tvlBeforeUsdNumber=${tvlBeforeUsdNumber}`
|
|
629
|
+
);
|
|
630
|
+
logger.verbose(
|
|
631
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => tvlNowInUSDC: ${tvlNowInUSDC.toString()}, tvlBeforeInUSDC: ${tvlBeforeInUSDC.toString()}`
|
|
632
|
+
);
|
|
633
|
+
logger.verbose(
|
|
634
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => tvlPerShareNow: ${tvlPerShareNow.toString()}, tvlPerShareBf: ${tvlPerShareBf.toString()}`
|
|
635
|
+
);
|
|
636
|
+
logger.verbose(
|
|
637
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => Supply before: ${supplyBefore.toString()}, Supply now (adjusted): ${adjustedSupplyNow.toString()}`
|
|
638
|
+
);
|
|
639
|
+
logger.verbose(
|
|
640
|
+
`${EkuboCLVault.name}: getUserRealizedAPY => Time diff in seconds: ${timeDiffSeconds}`
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
const apyForGivenBlocks =
|
|
644
|
+
Number(
|
|
645
|
+
tvlPerShareNow
|
|
646
|
+
.minus(tvlPerShareBf)
|
|
647
|
+
.multipliedBy(10000)
|
|
648
|
+
.dividedBy(tvlPerShareBf)
|
|
649
|
+
) / 10000;
|
|
650
|
+
|
|
651
|
+
return (apyForGivenBlocks * (365 * 24 * 3600)) / timeDiffSeconds;
|
|
652
|
+
*/
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
async getUserPositionCards(input: UserPositionCardsInput): Promise<UserPositionCard[]> {
|
|
656
|
+
const quoteToken = this.metadata.additionalInfo.quoteAsset;
|
|
657
|
+
const [userTVL, quotePrice] = await Promise.all([
|
|
658
|
+
this.getUserTVL(input.user),
|
|
659
|
+
this.pricer.getPrice(quoteToken.symbol),
|
|
660
|
+
]);
|
|
661
|
+
|
|
662
|
+
const token0IsQuote = userTVL.token0.tokenInfo.address.eq(quoteToken.address);
|
|
663
|
+
const token1IsQuote = userTVL.token1.tokenInfo.address.eq(quoteToken.address);
|
|
664
|
+
const token0QuoteAmount = token0IsQuote
|
|
665
|
+
? userTVL.token0.amount.toNumber()
|
|
666
|
+
: userTVL.token0.usdValue / (quotePrice.price || 1);
|
|
667
|
+
const token1QuoteAmount = token1IsQuote
|
|
668
|
+
? userTVL.token1.amount.toNumber()
|
|
669
|
+
: userTVL.token1.usdValue / (quotePrice.price || 1);
|
|
670
|
+
const totalQuoteAmount = token0QuoteAmount + token1QuoteAmount;
|
|
671
|
+
const quoteAmountDisplay = Number.isFinite(totalQuoteAmount)
|
|
672
|
+
? totalQuoteAmount.toLocaleString("en-US", {
|
|
673
|
+
maximumFractionDigits: quoteToken.displayDecimals ?? 2,
|
|
674
|
+
minimumFractionDigits: 0,
|
|
675
|
+
})
|
|
676
|
+
: "0";
|
|
677
|
+
const allocationValue = `${this.formatTokenAmountForCard(userTVL.token0.amount, userTVL.token0.tokenInfo)} / ${this.formatTokenAmountForCard(userTVL.token1.amount, userTVL.token1.tokenInfo)}`;
|
|
678
|
+
const allocationSubValue = `${this.formatUSDForCard(userTVL.token0.usdValue)} / ${this.formatUSDForCard(userTVL.token1.usdValue)}`;
|
|
679
|
+
|
|
680
|
+
const cards: UserPositionCard[] = [
|
|
681
|
+
{
|
|
682
|
+
title: "Your Holdings",
|
|
683
|
+
tooltip: `${quoteToken.symbol} equivalent value of your Ekubo position`,
|
|
684
|
+
value: `${quoteAmountDisplay} ${quoteToken.symbol}`,
|
|
685
|
+
subValue: `≈ ${this.formatUSDForCard(userTVL.usdValue)}`,
|
|
686
|
+
subValueColor: "positive",
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
title: "Holding Allocation",
|
|
690
|
+
tooltip: "Split of your position between token0 and token1",
|
|
691
|
+
value: allocationValue,
|
|
692
|
+
subValue: `≈ ${allocationSubValue}`,
|
|
693
|
+
subValueColor: "default",
|
|
694
|
+
},
|
|
695
|
+
];
|
|
696
|
+
return cards;
|
|
697
|
+
}
|
|
698
|
+
|
|
454
699
|
async feeBasedAPY(
|
|
455
700
|
timeperiod: '24h' | '7d' | '30d' | '3m' = '24h'
|
|
456
701
|
): Promise<number> {
|
|
@@ -485,20 +730,15 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
485
730
|
blockIdentifier: BlockIdentifier = "latest",
|
|
486
731
|
sinceBlocks = 600000,
|
|
487
732
|
timeperiod: '24h' | '7d' | '30d' | '3m' = '24h' // temp thing for fee based APY
|
|
488
|
-
): Promise<
|
|
489
|
-
|
|
733
|
+
): Promise<number> {
|
|
734
|
+
// ! switch to USDC later
|
|
735
|
+
const isUSDCQouteToken = this.metadata.additionalInfo.quoteAsset.symbol === "USDC.e" || this.metadata.additionalInfo.quoteAsset.symbol === "USDC";
|
|
490
736
|
if (!isUSDCQouteToken) {
|
|
491
737
|
// good for LSTs and stables
|
|
492
|
-
|
|
493
|
-
net: await this.netSharesBasedTrueAPY(blockIdentifier, sinceBlocks),
|
|
494
|
-
splits: []
|
|
495
|
-
};
|
|
738
|
+
return await this.netSharesBasedTrueAPY(blockIdentifier, sinceBlocks);
|
|
496
739
|
} else {
|
|
497
740
|
// good for non-stables
|
|
498
|
-
return
|
|
499
|
-
net: await this.feeBasedAPY(timeperiod),
|
|
500
|
-
splits: []
|
|
501
|
-
};
|
|
741
|
+
return await this.feeBasedAPY(timeperiod);
|
|
502
742
|
}
|
|
503
743
|
}
|
|
504
744
|
|
|
@@ -559,8 +799,14 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
559
799
|
assets.amount1.toString(),
|
|
560
800
|
token1Info.decimals
|
|
561
801
|
);
|
|
562
|
-
|
|
563
|
-
|
|
802
|
+
|
|
803
|
+
// Convert blockIdentifier to block number for pricer if it's a number
|
|
804
|
+
const blockNumber = typeof blockIdentifier === 'number' || typeof blockIdentifier === 'bigint'
|
|
805
|
+
? Number(blockIdentifier)
|
|
806
|
+
: undefined;
|
|
807
|
+
|
|
808
|
+
const P0 = await this.pricer.getPrice(token0Info.symbol, blockNumber);
|
|
809
|
+
const P1 = await this.pricer.getPrice(token1Info.symbol, blockNumber);
|
|
564
810
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
565
811
|
const token1Usd = Number(amount1.toFixed(13)) * P1.price;
|
|
566
812
|
|
|
@@ -627,8 +873,26 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
627
873
|
const P1 = await this.pricer.getPrice(token1Info.symbol);
|
|
628
874
|
const token0Usd = Number(amount0.toFixed(13)) * P0.price;
|
|
629
875
|
const token1Usd = Number(amount1.toFixed(13)) * P1.price;
|
|
876
|
+
const totalUsdValue = token0Usd + token1Usd;
|
|
877
|
+
|
|
878
|
+
if (
|
|
879
|
+
(totalUsdValue === 0 || token0Usd === 0 || token1Usd === 0 || amount0.eq(0) || amount1.eq(0)) &&
|
|
880
|
+
this.metadata.settings?.liveStatus === StrategyLiveStatus.ACTIVE
|
|
881
|
+
) {
|
|
882
|
+
logger.warn(
|
|
883
|
+
`${this.metadata.name}:getTVL - Zero value detected: ` +
|
|
884
|
+
`usdValue=${totalUsdValue}, ` +
|
|
885
|
+
`amount0=${amount0.toString()}, ` +
|
|
886
|
+
`amount1=${amount1.toString()}, ` +
|
|
887
|
+
`token0Price=${P0.price}, ` +
|
|
888
|
+
`token1Price=${P1.price}, ` +
|
|
889
|
+
`token0Usd=${token0Usd}, ` +
|
|
890
|
+
`token1Usd=${token1Usd}`
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
|
|
630
894
|
return {
|
|
631
|
-
usdValue:
|
|
895
|
+
usdValue: totalUsdValue,
|
|
632
896
|
token0: {
|
|
633
897
|
tokenInfo: token0Info,
|
|
634
898
|
amount: amount0,
|
|
@@ -1026,6 +1290,12 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1026
1290
|
amount1: availableAmount1.minus(y),
|
|
1027
1291
|
ratio: 0,
|
|
1028
1292
|
};
|
|
1293
|
+
} else if (ratio.eq(Infinity)) {
|
|
1294
|
+
return {
|
|
1295
|
+
amount0: availableAmount0,
|
|
1296
|
+
amount1: Web3Number.fromWei("0", availableAmount1.decimals),
|
|
1297
|
+
ratio: Infinity,
|
|
1298
|
+
};
|
|
1029
1299
|
}
|
|
1030
1300
|
return {
|
|
1031
1301
|
amount0: availableAmount0.plus(x),
|
|
@@ -1075,7 +1345,12 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1075
1345
|
};
|
|
1076
1346
|
}
|
|
1077
1347
|
|
|
1078
|
-
async getSwapInfoToHandleUnused(
|
|
1348
|
+
async getSwapInfoToHandleUnused(
|
|
1349
|
+
considerRebalance: boolean = true,
|
|
1350
|
+
newBounds: EkuboBounds | null = null,
|
|
1351
|
+
maxIterations = 20, priceRatioPrecision = 4,
|
|
1352
|
+
getQuoteCallback: (tokenToSell: string, tokenToBuy: string, amountWei: string, beneficiary: string) => Promise<Quote> = this.avnu.getQuotes
|
|
1353
|
+
): Promise<SwapInfo> {
|
|
1079
1354
|
const poolKey = await this.getPoolKey();
|
|
1080
1355
|
|
|
1081
1356
|
// fetch current unused balances of vault
|
|
@@ -1140,7 +1415,8 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1140
1415
|
token1Bal,
|
|
1141
1416
|
ekuboBounds,
|
|
1142
1417
|
maxIterations,
|
|
1143
|
-
priceRatioPrecision
|
|
1418
|
+
priceRatioPrecision,
|
|
1419
|
+
getQuoteCallback
|
|
1144
1420
|
);
|
|
1145
1421
|
}
|
|
1146
1422
|
|
|
@@ -1186,6 +1462,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1186
1462
|
const tokenToSell = expectedAmounts.amount0.lessThan(token0Bal)
|
|
1187
1463
|
? poolKey.token0
|
|
1188
1464
|
: poolKey.token1;
|
|
1465
|
+
logger.verbose(`getSwapParams => tokenToSell: ${tokenToSell.address}, expectedAmounts: ${expectedAmounts.amount0.toString()}, bal0: ${token0Bal.toString()}`);
|
|
1189
1466
|
// The other token is the one to buy
|
|
1190
1467
|
const tokenToBuy =
|
|
1191
1468
|
tokenToSell == poolKey.token0 ? poolKey.token1 : poolKey.token0;
|
|
@@ -1210,13 +1487,13 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1210
1487
|
/**
|
|
1211
1488
|
* @description Calculates swap info based on given amounts of token0 and token1
|
|
1212
1489
|
* Use token0 and token1 balances to determine the expected amounts for new bounds
|
|
1213
|
-
* @param poolKey
|
|
1214
|
-
* @param token0Bal
|
|
1215
|
-
* @param token1Bal
|
|
1490
|
+
* @param poolKey
|
|
1491
|
+
* @param token0Bal
|
|
1492
|
+
* @param token1Bal
|
|
1216
1493
|
* @param bounds // new bounds
|
|
1217
|
-
* @param maxIterations
|
|
1494
|
+
* @param maxIterations
|
|
1218
1495
|
* @returns {Promise<SwapInfo>}
|
|
1219
|
-
*
|
|
1496
|
+
*
|
|
1220
1497
|
*/
|
|
1221
1498
|
async getSwapInfoGivenAmounts(
|
|
1222
1499
|
poolKey: EkuboPoolKey,
|
|
@@ -1224,7 +1501,8 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1224
1501
|
token1Bal: Web3Number,
|
|
1225
1502
|
bounds: EkuboBounds,
|
|
1226
1503
|
maxIterations: number = 20,
|
|
1227
|
-
priceRatioPrecision: number = 4
|
|
1504
|
+
priceRatioPrecision: number = 4,
|
|
1505
|
+
getQuoteCallback: (tokenToSell: string, tokenToBuy: string, amountWei: string, beneficiary: string) => Promise<Quote> = this.avnu.getQuotes
|
|
1228
1506
|
): Promise<SwapInfo> {
|
|
1229
1507
|
logger.verbose(
|
|
1230
1508
|
`${
|
|
@@ -1279,12 +1557,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1279
1557
|
}
|
|
1280
1558
|
|
|
1281
1559
|
// Get a quote for swapping the calculated amount
|
|
1282
|
-
const quote = await this.
|
|
1283
|
-
tokenToSell.address,
|
|
1284
|
-
tokenToBuy.address,
|
|
1285
|
-
amountToSell.toWei(),
|
|
1286
|
-
this.address.address
|
|
1287
|
-
);
|
|
1560
|
+
const quote = await getQuoteCallback(tokenToSell.address, tokenToBuy.address, amountToSell.toWei(), this.address.address);
|
|
1288
1561
|
|
|
1289
1562
|
// If all of the token is to be swapped, return the swap info directly
|
|
1290
1563
|
if (remainingSellAmount.eq(0)) {
|
|
@@ -1394,8 +1667,11 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1394
1667
|
* @param retry - Current retry attempt number (default 0)
|
|
1395
1668
|
* @param adjustmentFactor - Percentage to adjust swap amount by (default 1)
|
|
1396
1669
|
* @param isToken0Deficit - Whether token0 balance needs increasing (default true)
|
|
1670
|
+
* @param MAX_RETRIES - Maximum number of retries (default 40)
|
|
1671
|
+
* @param sameErrorCount - For certain errors, we just retry with same amount again. This is the count of such retries (default { count: 0, error: null })
|
|
1672
|
+
* @param MAX_SAME_ERROR_COUNT - For certain errors, we just retry with same amount again. This limits such retries (default 10)
|
|
1397
1673
|
* @returns Array of contract calls needed for rebalancing
|
|
1398
|
-
* @throws Error if max retries reached without successful rebalance
|
|
1674
|
+
* @throws Error if max retries reached without successful rebalance or max same errors reached
|
|
1399
1675
|
*/
|
|
1400
1676
|
async rebalanceIter(
|
|
1401
1677
|
swapInfo: SwapInfo,
|
|
@@ -1405,15 +1681,23 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1405
1681
|
retry = 0,
|
|
1406
1682
|
lowerLimit = 0n,
|
|
1407
1683
|
upperLimit = 0n,
|
|
1408
|
-
MAX_RETRIES = 40
|
|
1684
|
+
MAX_RETRIES = 40,
|
|
1685
|
+
sameErrorCount: { count: number, error: null | string } = { count: 0, error: null },
|
|
1686
|
+
MAX_SAME_ERROR_COUNT = 10
|
|
1409
1687
|
): Promise<Call[]> {
|
|
1410
1688
|
|
|
1411
1689
|
logger.verbose(
|
|
1412
1690
|
`Rebalancing ${this.metadata.name}: ` +
|
|
1413
|
-
`retry=${retry}, lowerLimit=${lowerLimit}, upperLimit=${upperLimit}, isSellTokenToken0=${isSellTokenToken0}`
|
|
1691
|
+
`retry=${retry}, lowerLimit=${lowerLimit}, upperLimit=${upperLimit}, isSellTokenToken0=${isSellTokenToken0}, MAX_RETRIES=${MAX_RETRIES}, sameErrorCount=${sameErrorCount.error} (${sameErrorCount.count})`
|
|
1414
1692
|
);
|
|
1415
1693
|
|
|
1694
|
+
if (sameErrorCount.count >= MAX_SAME_ERROR_COUNT) {
|
|
1695
|
+
logger.error(`Rebalance failed after ${MAX_SAME_ERROR_COUNT} same errors`);
|
|
1696
|
+
throw new Error(`Rebalance failed after ${MAX_SAME_ERROR_COUNT} same errors`);
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1416
1699
|
const fromAmount = uint256.uint256ToBN(swapInfo.token_from_amount);
|
|
1700
|
+
const fromTokenInfo = await Global.getTokenInfoFromAddr(ContractAddr.from(swapInfo.token_from_address));
|
|
1417
1701
|
logger.verbose(
|
|
1418
1702
|
`Selling ${fromAmount.toString()} of token ${swapInfo.token_from_address}`
|
|
1419
1703
|
);
|
|
@@ -1433,8 +1717,8 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1433
1717
|
);
|
|
1434
1718
|
|
|
1435
1719
|
const newSwapInfo = { ...swapInfo };
|
|
1436
|
-
const currentAmount = Web3Number.fromWei(fromAmount.toString(),
|
|
1437
|
-
logger.verbose(`Current amount: ${currentAmount.toString()}`);
|
|
1720
|
+
const currentAmount = Web3Number.fromWei(fromAmount.toString(), fromTokenInfo.decimals);
|
|
1721
|
+
logger.verbose(`Current amount: ${currentAmount.toString()}, lowerLimit: ${lowerLimit.toString()}, upperLimit: ${upperLimit.toString()}`);
|
|
1438
1722
|
if (
|
|
1439
1723
|
err.message.includes("invalid token0 balance") ||
|
|
1440
1724
|
err.message.includes("invalid token0 amount")
|
|
@@ -1487,11 +1771,43 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1487
1771
|
}
|
|
1488
1772
|
newSwapInfo.token_from_amount = uint256.bnToUint256(nextAmount);
|
|
1489
1773
|
}
|
|
1774
|
+
} else if (err.message.includes("Residual tokens")) {
|
|
1775
|
+
logger.error("Residual tokens");
|
|
1776
|
+
if (sameErrorCount.error == "Residual tokens") {
|
|
1777
|
+
sameErrorCount.count++;
|
|
1778
|
+
} else {
|
|
1779
|
+
sameErrorCount.error = "Residual tokens";
|
|
1780
|
+
sameErrorCount.count = 1;
|
|
1781
|
+
}
|
|
1782
|
+
// dont do anything, just try again.
|
|
1783
|
+
} else if (err.message.includes("Insufficient tokens received")) {
|
|
1784
|
+
logger.error("Insufficient tokens received");
|
|
1785
|
+
if (sameErrorCount.error == "Insufficient tokens received") {
|
|
1786
|
+
sameErrorCount.count++;
|
|
1787
|
+
} else {
|
|
1788
|
+
sameErrorCount.error = "Insufficient tokens received";
|
|
1789
|
+
sameErrorCount.count = 1;
|
|
1790
|
+
}
|
|
1791
|
+
// dont do anything, just try again.
|
|
1792
|
+
} else if (err.message.includes("Could not reach the end of the program")) {
|
|
1793
|
+
logger.error("Could not reach the end of the program, may be the block is full (could be a temp/permanent gas issue)");
|
|
1794
|
+
if (sameErrorCount.error == "Could not reach the end of the program") {
|
|
1795
|
+
sameErrorCount.count++;
|
|
1796
|
+
} else {
|
|
1797
|
+
sameErrorCount.error = "Could not reach the end of the program";
|
|
1798
|
+
sameErrorCount.count = 1;
|
|
1799
|
+
}
|
|
1800
|
+
// just try again.
|
|
1490
1801
|
} else {
|
|
1491
1802
|
logger.error("Unexpected error:", err);
|
|
1492
1803
|
throw err;
|
|
1493
1804
|
}
|
|
1494
1805
|
newSwapInfo.token_to_min_amount = uint256.bnToUint256("0");
|
|
1806
|
+
|
|
1807
|
+
// if (uint256.uint256ToBN(newSwapInfo.token_from_amount) == fromAmount && sameErrorCount.error == 'loop-stuck') {
|
|
1808
|
+
// logger.error("Swap amount did not change, cannot proceed");
|
|
1809
|
+
// sameErrorCount = { count: MAX_SAME_ERROR_COUNT, error: null };
|
|
1810
|
+
// }
|
|
1495
1811
|
return this.rebalanceIter(
|
|
1496
1812
|
newSwapInfo,
|
|
1497
1813
|
acc,
|
|
@@ -1499,7 +1815,10 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1499
1815
|
isSellTokenToken0,
|
|
1500
1816
|
retry + 1,
|
|
1501
1817
|
lowerLimit,
|
|
1502
|
-
upperLimit
|
|
1818
|
+
upperLimit,
|
|
1819
|
+
MAX_RETRIES,
|
|
1820
|
+
sameErrorCount,
|
|
1821
|
+
MAX_SAME_ERROR_COUNT
|
|
1503
1822
|
);
|
|
1504
1823
|
}
|
|
1505
1824
|
}
|
|
@@ -1520,8 +1839,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1520
1839
|
|
|
1521
1840
|
static priceToSqrtRatio(price: number) {
|
|
1522
1841
|
return (
|
|
1523
|
-
(BigInt(Math.
|
|
1524
|
-
BigInt(1e9)
|
|
1842
|
+
(BigInt(Math.sqrt(price) * 2 ** 128))
|
|
1525
1843
|
);
|
|
1526
1844
|
}
|
|
1527
1845
|
|
|
@@ -1592,20 +1910,34 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1592
1910
|
};
|
|
1593
1911
|
}
|
|
1594
1912
|
|
|
1595
|
-
async
|
|
1913
|
+
async getPendingRewards(): Promise<HarvestInfo[]> {
|
|
1596
1914
|
const ekuboHarvests = new EkuboHarvests(this.config);
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1915
|
+
return await ekuboHarvests.getUnHarvestedRewards(this.address);
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
async harvest(acc: Account, maxIterations = 20, priceRatioPrecision = 4, minRewardAmount: Web3Number = new Web3Number(0, 18)): Promise<Call[]> {
|
|
1919
|
+
const _pendingRewards = await this.getPendingRewards();
|
|
1920
|
+
const pendingRewards = _pendingRewards.filter(claim => claim.actualReward.greaterThanOrEqualTo(minRewardAmount));
|
|
1921
|
+
if (pendingRewards.length == 0) {
|
|
1922
|
+
logger.verbose(`${EkuboCLVault.name}: harvest => no pending rewards found`);
|
|
1923
|
+
return [];
|
|
1924
|
+
}
|
|
1925
|
+
// get necessary info for the harvest
|
|
1600
1926
|
const poolKey = await this.getPoolKey();
|
|
1601
1927
|
const token0Info = await Global.getTokenInfoFromAddr(poolKey.token0);
|
|
1602
1928
|
const token1Info = await Global.getTokenInfoFromAddr(poolKey.token1);
|
|
1603
1929
|
const bounds = await this.getCurrentBounds();
|
|
1604
1930
|
logger.verbose(
|
|
1605
|
-
`${EkuboCLVault.name}: harvest => unClaimedRewards: ${
|
|
1931
|
+
`${EkuboCLVault.name}: harvest => unClaimedRewards: ${pendingRewards.length}`
|
|
1606
1932
|
);
|
|
1933
|
+
|
|
1934
|
+
// execute the harvest
|
|
1607
1935
|
const calls: Call[] = [];
|
|
1608
|
-
|
|
1936
|
+
// do one at a time.
|
|
1937
|
+
const chosenClaim = pendingRewards[0];
|
|
1938
|
+
logger.info(`${EkuboCLVault.name}: harvest => doing one at a time`);
|
|
1939
|
+
logger.info(`${EkuboCLVault.name}: harvest => chosenClaim -> Claim ID: ${chosenClaim.claim.id}, Amount: ${chosenClaim.claim.amount.toString()}, actualAmount: ${chosenClaim.actualReward.toString()}, addr: ${chosenClaim.claim.claimee.toString()}`);
|
|
1940
|
+
for (let claim of [chosenClaim]) {
|
|
1609
1941
|
const fee = claim.claim.amount
|
|
1610
1942
|
.multipliedBy(this.metadata.additionalInfo.feeBps)
|
|
1611
1943
|
.dividedBy(10000);
|
|
@@ -1653,8 +1985,8 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1653
1985
|
|
|
1654
1986
|
/**
|
|
1655
1987
|
* @description This funciton requires atleast one of the pool tokens to be reward token
|
|
1656
|
-
* i.e. STRK.
|
|
1657
|
-
* @param params
|
|
1988
|
+
* i.e. STRK.
|
|
1989
|
+
* @param params
|
|
1658
1990
|
*/
|
|
1659
1991
|
async _handleRewardAndVaultTokenMatchHarvest(params: {
|
|
1660
1992
|
claim: HarvestInfo;
|
|
@@ -1678,60 +2010,50 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1678
2010
|
logger.verbose(
|
|
1679
2011
|
`${
|
|
1680
2012
|
EkuboCLVault.name
|
|
1681
|
-
}: harvest => token0Amt: ${token0Amt.
|
|
1682
|
-
);
|
|
1683
|
-
|
|
1684
|
-
// THis function cannot handle swapping of non-STRK pool,
|
|
1685
|
-
// bcz atleast one of token0Amt or token1Amt are in STRK terms.
|
|
1686
|
-
const swapInfo = await this.getSwapInfoGivenAmounts(
|
|
1687
|
-
poolKey,
|
|
1688
|
-
token0Amt,
|
|
1689
|
-
token1Amt,
|
|
1690
|
-
bounds,
|
|
1691
|
-
maxIterations,
|
|
1692
|
-
priceRatioPrecision
|
|
1693
|
-
);
|
|
1694
|
-
swapInfo.token_to_address = token0Info.address.address;
|
|
1695
|
-
logger.verbose(
|
|
1696
|
-
`${EkuboCLVault.name}: harvest => swapInfo: ${JSON.stringify(swapInfo)}`
|
|
2013
|
+
}: harvest => token0Amt: ${token0Amt.toFixed(18)}, token1Amt: ${token1Amt.toFixed(18)}`
|
|
1697
2014
|
);
|
|
1698
2015
|
|
|
1699
2016
|
logger.verbose(
|
|
1700
2017
|
`${EkuboCLVault.name}: harvest => claim: ${JSON.stringify(claim)}`
|
|
1701
2018
|
);
|
|
1702
|
-
const
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
2019
|
+
const claimTokenInfo = await Global.getTokenInfoFromAddr(claim.token);
|
|
2020
|
+
const harvestEstimateCall = async (baseSwapInfo: SwapInfo) => {
|
|
2021
|
+
// - the base swap if actual swap from claim token to non-claim token
|
|
2022
|
+
// - the other swap is just claim token to claim token (e.g. STRK to STRK)
|
|
2023
|
+
// which is just dummy
|
|
2024
|
+
let baseSwapAmount = Web3Number.fromWei(
|
|
2025
|
+
uint256.uint256ToBN(baseSwapInfo.token_from_amount).toString(),
|
|
2026
|
+
claimTokenInfo.decimals
|
|
1706
2027
|
).minimum(
|
|
1707
|
-
postFeeAmount.toFixed(
|
|
2028
|
+
postFeeAmount.toFixed(claimTokenInfo.decimals)
|
|
1708
2029
|
); // ensure we don't swap more than we have
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
2030
|
+
if (baseSwapAmount.lt(0.0001)) {
|
|
2031
|
+
baseSwapAmount = new Web3Number(0, claimTokenInfo.decimals);
|
|
2032
|
+
}
|
|
2033
|
+
baseSwapInfo.token_from_amount = uint256.bnToUint256(baseSwapAmount.toWei());
|
|
1713
2034
|
|
|
2035
|
+
const isToken0ClaimToken = claim.token.eq(poolKey.token0);
|
|
1714
2036
|
logger.verbose(
|
|
1715
|
-
`${EkuboCLVault.name}: harvest =>
|
|
2037
|
+
`${EkuboCLVault.name}: harvest => isToken0ClaimToken: ${isToken0ClaimToken}, baseSwapAmount: ${baseSwapAmount}`
|
|
1716
2038
|
);
|
|
1717
2039
|
|
|
1718
|
-
const remainingAmount = postFeeAmount.minus(
|
|
2040
|
+
const remainingAmount = postFeeAmount.minus(baseSwapAmount).maximum(0);
|
|
1719
2041
|
logger.verbose(
|
|
1720
2042
|
`${EkuboCLVault.name}: harvest => remainingAmount: ${remainingAmount}`
|
|
1721
2043
|
);
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
2044
|
+
|
|
2045
|
+
// obv, same to same
|
|
2046
|
+
let dummySwapInfo = AvnuWrapper.buildZeroSwap(claim.token, this.address.address, claim.token);
|
|
2047
|
+
dummySwapInfo.token_from_amount = uint256.bnToUint256(remainingAmount.toWei());
|
|
2048
|
+
|
|
1727
2049
|
logger.verbose(
|
|
1728
|
-
`${EkuboCLVault.name}: harvest =>
|
|
1729
|
-
|
|
2050
|
+
`${EkuboCLVault.name}: harvest => dummySwapInfo: ${JSON.stringify(
|
|
2051
|
+
dummySwapInfo
|
|
1730
2052
|
)}`
|
|
1731
2053
|
);
|
|
1732
2054
|
logger.verbose(
|
|
1733
|
-
`${EkuboCLVault.name}: harvest =>
|
|
1734
|
-
|
|
2055
|
+
`${EkuboCLVault.name}: harvest => baseSwapInfo: ${JSON.stringify(
|
|
2056
|
+
baseSwapInfo
|
|
1735
2057
|
)}`
|
|
1736
2058
|
);
|
|
1737
2059
|
const calldata = [
|
|
@@ -1742,18 +2064,41 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1742
2064
|
claimee: claim.claim.claimee.address,
|
|
1743
2065
|
},
|
|
1744
2066
|
claim.proof.map((p) => num.getDecimalString(p)),
|
|
1745
|
-
|
|
1746
|
-
|
|
2067
|
+
isToken0ClaimToken ? dummySwapInfo : baseSwapInfo, // is token0 claim token, its just dummy swap
|
|
2068
|
+
isToken0ClaimToken ? baseSwapInfo : dummySwapInfo,
|
|
1747
2069
|
];
|
|
1748
|
-
logger.verbose(
|
|
1749
|
-
`${EkuboCLVault.name}: harvest => calldata: ${JSON.stringify(
|
|
1750
|
-
calldata
|
|
1751
|
-
)}`
|
|
1752
|
-
);
|
|
1753
2070
|
return [this.contract.populate("harvest", calldata)];
|
|
1754
2071
|
};
|
|
2072
|
+
|
|
2073
|
+
// if token0 == claim token, then the base swapInfo is from claim token to token1
|
|
2074
|
+
// if token1 == claim token, then the base swapInfo is from claim token to token0
|
|
2075
|
+
const isToken0ClaimToken = claim.token.eq(poolKey.token0);
|
|
2076
|
+
let baseSwapInfo = AvnuWrapper.buildZeroSwap(claim.token, this.address.address, isToken0ClaimToken ? token1Info.address : token0Info.address);
|
|
2077
|
+
baseSwapInfo.token_from_amount = uint256.bnToUint256(postFeeAmount.toWei()); // we try to swap all to start with
|
|
2078
|
+
|
|
2079
|
+
// if token0 != claim token, then we swap from claim token to token0
|
|
2080
|
+
if (postFeeAmount.greaterThan(0) && !isToken0ClaimToken) {
|
|
2081
|
+
const avnuWrapper = new AvnuWrapper();
|
|
2082
|
+
const quote = await avnuWrapper.getQuotes(
|
|
2083
|
+
claim.token.address,
|
|
2084
|
+
token0Info.address.address,
|
|
2085
|
+
postFeeAmount.toWei(),
|
|
2086
|
+
this.address.address
|
|
2087
|
+
);
|
|
2088
|
+
baseSwapInfo = await avnuWrapper.getSwapInfo(quote, this.address.address, 0, this.address.address);
|
|
2089
|
+
} else if (postFeeAmount.greaterThan(0) && isToken0ClaimToken) {
|
|
2090
|
+
// if token0 == claim token, then we swap from claim token to token1
|
|
2091
|
+
const avnuWrapper = new AvnuWrapper();
|
|
2092
|
+
const quote = await avnuWrapper.getQuotes(
|
|
2093
|
+
claim.token.address,
|
|
2094
|
+
token1Info.address.address,
|
|
2095
|
+
postFeeAmount.toWei(),
|
|
2096
|
+
this.address.address
|
|
2097
|
+
);
|
|
2098
|
+
baseSwapInfo = await avnuWrapper.getSwapInfo(quote, this.address.address, 0, this.address.address);
|
|
2099
|
+
}
|
|
1755
2100
|
const _callsFinal = await this.rebalanceIter(
|
|
1756
|
-
|
|
2101
|
+
baseSwapInfo,
|
|
1757
2102
|
acc,
|
|
1758
2103
|
harvestEstimateCall,
|
|
1759
2104
|
claim.token.eq(poolKey.token0),
|
|
@@ -1772,9 +2117,9 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1772
2117
|
|
|
1773
2118
|
/**
|
|
1774
2119
|
* @description This function handles harvesting of reward token that is not the same as any of the vault token
|
|
1775
|
-
* i.e. STRK is not part of vault tokens like BTC/ETH
|
|
1776
|
-
* @param params
|
|
1777
|
-
* @returns
|
|
2120
|
+
* i.e. STRK is not part of vault tokens like BTC/ETH
|
|
2121
|
+
* @param params
|
|
2122
|
+
* @returns
|
|
1778
2123
|
*/
|
|
1779
2124
|
async _handleRewardAndVaultTokenMismatchHarvest(params: {
|
|
1780
2125
|
claim: HarvestInfo;
|
|
@@ -1812,7 +2157,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1812
2157
|
return [harvestCall];
|
|
1813
2158
|
}
|
|
1814
2159
|
|
|
1815
|
-
// given an amount (i.e. portion of reward to use to swap to token0), returns info on increasing or decreasing
|
|
2160
|
+
// given an amount (i.e. portion of reward to use to swap to token0), returns info on increasing or decreasing
|
|
1816
2161
|
// amount for binary search
|
|
1817
2162
|
async harvestMismatchEstimateCallFn(params: {
|
|
1818
2163
|
postFeeAmount: Web3Number;
|
|
@@ -1823,39 +2168,47 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1823
2168
|
}) {
|
|
1824
2169
|
const { postFeeAmount, claim, token0Info, token1Info, acc } = params;
|
|
1825
2170
|
let harvestCall: Call | null = null;
|
|
2171
|
+
logger.verbose(`${EkuboCLVault.name}: harvestMismatchEstimateCallFn => postFeeAmount: ${postFeeAmount.toString()}`);
|
|
1826
2172
|
|
|
2173
|
+
let attempt = 0;
|
|
2174
|
+
let MAX_ATTEMPTS = 50;
|
|
1827
2175
|
const binarySearchCallbackFn = async (mid: bigint) => {
|
|
2176
|
+
attempt++;
|
|
2177
|
+
logger.verbose(`${EkuboCLVault.name}: harvestMismatchEstimateCallFn => mid: ${mid}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
1828
2178
|
const rewardPart2 = BigInt(postFeeAmount.toWei()) - mid;
|
|
1829
2179
|
const avnuWrapper = new AvnuWrapper();
|
|
1830
2180
|
const beneficiary = this.address.address;
|
|
1831
2181
|
|
|
1832
2182
|
// get quote for 1st part
|
|
1833
|
-
const
|
|
2183
|
+
const quote1Prom = avnuWrapper.getQuotes(
|
|
1834
2184
|
claim.token.address,
|
|
1835
2185
|
token0Info.address.address,
|
|
1836
2186
|
mid.toString(),
|
|
1837
2187
|
beneficiary
|
|
1838
2188
|
);
|
|
2189
|
+
const quote2Prom = avnuWrapper.getQuotes(
|
|
2190
|
+
claim.token.address,
|
|
2191
|
+
token1Info.address.address,
|
|
2192
|
+
rewardPart2.toString(),
|
|
2193
|
+
beneficiary
|
|
2194
|
+
);
|
|
2195
|
+
const [quote1, quote2] = await Promise.all([quote1Prom, quote2Prom]);
|
|
2196
|
+
|
|
1839
2197
|
// default min amount is ok
|
|
1840
2198
|
const swapInfo1 = await avnuWrapper.getSwapInfo(
|
|
1841
2199
|
quote1,
|
|
1842
2200
|
beneficiary,
|
|
1843
|
-
0,
|
|
2201
|
+
0, // fee bps
|
|
1844
2202
|
beneficiary
|
|
1845
2203
|
);
|
|
1846
2204
|
|
|
1847
2205
|
// get quote for 2nd part
|
|
1848
|
-
|
|
1849
|
-
claim.token.address,
|
|
1850
|
-
token1Info.address.address,
|
|
1851
|
-
rewardPart2.toString(),
|
|
1852
|
-
beneficiary
|
|
1853
|
-
);
|
|
2206
|
+
|
|
1854
2207
|
// default min amount is ok
|
|
1855
2208
|
const swapInfo2 = await avnuWrapper.getSwapInfo(
|
|
1856
2209
|
quote2,
|
|
1857
2210
|
beneficiary,
|
|
1858
|
-
0,
|
|
2211
|
+
0, // fee bps
|
|
1859
2212
|
beneficiary
|
|
1860
2213
|
);
|
|
1861
2214
|
|
|
@@ -1874,27 +2227,36 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1874
2227
|
];
|
|
1875
2228
|
harvestCall = this.contract.populate("harvest", calldata)
|
|
1876
2229
|
const gas = await acc.estimateInvokeFee(harvestCall);
|
|
2230
|
+
logger.verbose(`${EkuboCLVault.name}: harvestMismatchEstimateCallFn => gas: ${gas.overall_fee.toString()}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
1877
2231
|
return 'found';
|
|
1878
2232
|
} catch (err: any) {
|
|
1879
2233
|
if (err.message.includes('invalid token0 amount')) {
|
|
2234
|
+
logger.verbose(`${EkuboCLVault.name}: harvestMismatchEstimateCallFn => invalid token0 amount, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
1880
2235
|
// too much token0 amount left, may be swap less to token0
|
|
1881
2236
|
return 'go_low';
|
|
1882
2237
|
} else if (err.message.includes('invalid token1 amount')) {
|
|
2238
|
+
logger.verbose(`${EkuboCLVault.name}: harvestMismatchEstimateCallFn => invalid token1 amount, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
1883
2239
|
// too much token1 balance left, may be swap more to token0
|
|
1884
2240
|
return 'go_high';
|
|
1885
2241
|
}
|
|
2242
|
+
logger.verbose(`${EkuboCLVault.name}: harvestMismatchEstimateCallFn => error: ${err.message}, attempt: ${attempt}/${MAX_ATTEMPTS}`);
|
|
1886
2243
|
return 'retry';
|
|
1887
2244
|
}
|
|
1888
2245
|
}
|
|
1889
2246
|
|
|
1890
2247
|
// run the binary search
|
|
1891
2248
|
await binarySearch(0n, BigInt(postFeeAmount.toWei()), binarySearchCallbackFn);
|
|
1892
|
-
|
|
2249
|
+
|
|
1893
2250
|
return harvestCall;
|
|
1894
2251
|
}
|
|
1895
2252
|
|
|
1896
2253
|
async getInvestmentFlows() {
|
|
1897
|
-
|
|
2254
|
+
// for LSTs, we use 30d, else 7d for the yield calculation
|
|
2255
|
+
// TODO Make the block compute more dynamic
|
|
2256
|
+
const blocksDiff = this.metadata.additionalInfo.lstContract
|
|
2257
|
+
? 600000
|
|
2258
|
+
: 600000 / 4;
|
|
2259
|
+
const netYield = await this.netAPY("latest", blocksDiff, "7d" as any);
|
|
1898
2260
|
const poolKey = await this.getPoolKey();
|
|
1899
2261
|
|
|
1900
2262
|
const linkedFlow: IInvestmentFlow = {
|
|
@@ -1915,7 +2277,7 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1915
2277
|
id: "base",
|
|
1916
2278
|
title: "Your Deposit",
|
|
1917
2279
|
subItems: [
|
|
1918
|
-
{ key: `Net yield`, value: `${(netYield
|
|
2280
|
+
{ key: `Net yield`, value: `${(netYield * 100).toFixed(2)}%` },
|
|
1919
2281
|
{
|
|
1920
2282
|
key: `Performance Fee`,
|
|
1921
2283
|
value: `${(this.metadata.additionalInfo.feeBps / 100).toFixed(2)}%`,
|
|
@@ -1931,8 +2293,8 @@ export class EkuboCLVault extends BaseStrategy<
|
|
|
1931
2293
|
subItems: [
|
|
1932
2294
|
{
|
|
1933
2295
|
key: "Range selection",
|
|
1934
|
-
value: (typeof this.metadata.additionalInfo.newBounds == 'string') ?
|
|
1935
|
-
this.metadata.additionalInfo.newBounds :
|
|
2296
|
+
value: (typeof this.metadata.additionalInfo.newBounds == 'string') ?
|
|
2297
|
+
this.metadata.additionalInfo.newBounds :
|
|
1936
2298
|
`${
|
|
1937
2299
|
this.metadata.additionalInfo.newBounds.lower *
|
|
1938
2300
|
Number(poolKey.tick_spacing)
|
|
@@ -2053,7 +2415,11 @@ function getLSTFAQs(lstSymbol: string): FAQ[] {
|
|
|
2053
2415
|
]
|
|
2054
2416
|
}
|
|
2055
2417
|
|
|
2418
|
+
const vaultTypeDescription = 'Automatically collects fees and rebalances positions on Ekubo to optimize yield';
|
|
2419
|
+
const vaultType = VaultType.AUTOMATED_LP;
|
|
2420
|
+
|
|
2056
2421
|
const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
|
|
2422
|
+
id: "ekubo_cl_xstrkstrk",
|
|
2057
2423
|
name: "Ekubo xSTRK/STRK",
|
|
2058
2424
|
description: <></>,
|
|
2059
2425
|
address: ContractAddr.from(
|
|
@@ -2061,6 +2427,10 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
|
|
|
2061
2427
|
),
|
|
2062
2428
|
launchBlock: 1209881,
|
|
2063
2429
|
type: "Other",
|
|
2430
|
+
vaultType: {
|
|
2431
|
+
type: vaultType,
|
|
2432
|
+
description: vaultTypeDescription
|
|
2433
|
+
},
|
|
2064
2434
|
// must be same order as poolKey token0 and token1
|
|
2065
2435
|
depositTokens: [
|
|
2066
2436
|
Global.getDefaultTokens().find((t) => t.symbol === "xSTRK")!,
|
|
@@ -2068,7 +2438,7 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
|
|
|
2068
2438
|
],
|
|
2069
2439
|
protocols: [_protocol],
|
|
2070
2440
|
auditUrl: AUDIT_URL,
|
|
2071
|
-
|
|
2441
|
+
curator: UnwrapLabsCurator,
|
|
2072
2442
|
risk: {
|
|
2073
2443
|
riskFactor: _lstPoolRiskFactors,
|
|
2074
2444
|
netRisk:
|
|
@@ -2078,6 +2448,10 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
|
|
|
2078
2448
|
},
|
|
2079
2449
|
apyMethodology:
|
|
2080
2450
|
"APY based on 30-day historical performance, including fees and rewards.",
|
|
2451
|
+
realizedApyMethodology: "The realizedAPY is based on past 14 days performance by the vault",
|
|
2452
|
+
feeBps: {
|
|
2453
|
+
performanceFeeBps: 1000,
|
|
2454
|
+
},
|
|
2081
2455
|
additionalInfo: {
|
|
2082
2456
|
newBounds: {
|
|
2083
2457
|
lower: -1,
|
|
@@ -2094,270 +2468,589 @@ const xSTRKSTRK: IStrategyMetadata<CLVaultStrategySettings> = {
|
|
|
2094
2468
|
},
|
|
2095
2469
|
quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
|
|
2096
2470
|
},
|
|
2471
|
+
settings: {
|
|
2472
|
+
isAudited: true,
|
|
2473
|
+
isPaused: false,
|
|
2474
|
+
liveStatus: StrategyLiveStatus.ACTIVE,
|
|
2475
|
+
isInstantWithdrawal: true,
|
|
2476
|
+
hideNetEarnings: true,
|
|
2477
|
+
isTransactionHistDisabled: true,
|
|
2478
|
+
quoteToken: Global.getDefaultTokens().find((t) => t.symbol === "STRK")!,
|
|
2479
|
+
alerts: [
|
|
2480
|
+
{
|
|
2481
|
+
type: "info",
|
|
2482
|
+
text: (
|
|
2483
|
+
<p>
|
|
2484
|
+
Depending on the current position range and price, your
|
|
2485
|
+
input amounts are automatically adjusted to nearest
|
|
2486
|
+
required amounts. If you have insufficient tokens, you
|
|
2487
|
+
can acquire the required tokens on{" "}
|
|
2488
|
+
<a
|
|
2489
|
+
href="https://avnu.fi"
|
|
2490
|
+
target="_blank"
|
|
2491
|
+
rel="noopener noreferrer"
|
|
2492
|
+
>
|
|
2493
|
+
Avnu
|
|
2494
|
+
</a>
|
|
2495
|
+
</p>
|
|
2496
|
+
),
|
|
2497
|
+
tab: "deposit"
|
|
2498
|
+
},
|
|
2499
|
+
{
|
|
2500
|
+
type: "info",
|
|
2501
|
+
text: (
|
|
2502
|
+
<>
|
|
2503
|
+
Depending on the current position range and price, you
|
|
2504
|
+
may receive both of the tokens or one of the tokens
|
|
2505
|
+
depending on the price
|
|
2506
|
+
</>
|
|
2507
|
+
),
|
|
2508
|
+
tab: "withdraw"
|
|
2509
|
+
}
|
|
2510
|
+
],
|
|
2511
|
+
tags: [StrategyTag.AUTOMATED_LP]
|
|
2512
|
+
},
|
|
2097
2513
|
faqs: getLSTFAQs("xSTRK"),
|
|
2098
2514
|
points: [{
|
|
2099
|
-
multiplier:
|
|
2515
|
+
multiplier: 15,
|
|
2100
2516
|
logo: 'https://endur.fi/favicon.ico',
|
|
2101
2517
|
toolTip: "This strategy holds xSTRK and STRK tokens. Earn 1x Endur points on your xSTRK portion of Liquidity. STRK portion will earn Endur's DEX Bonus points. Points can be found on endur.fi.",
|
|
2102
2518
|
}],
|
|
2103
2519
|
contractDetails: [],
|
|
2104
|
-
investmentSteps: []
|
|
2520
|
+
investmentSteps: [],
|
|
2521
|
+
tags: [StrategyTag.AUTOMATED_LP],
|
|
2522
|
+
security: {
|
|
2523
|
+
auditStatus: AuditStatus.AUDITED,
|
|
2524
|
+
sourceCode: {
|
|
2525
|
+
type: SourceCodeType.OPEN_SOURCE,
|
|
2526
|
+
contractLink: "https://github.com/trovesfi/troves-contracts",
|
|
2527
|
+
},
|
|
2528
|
+
accessControl: {
|
|
2529
|
+
type: AccessControlType.ROLE_BASED_ACCESS,
|
|
2530
|
+
addresses: [MY_ACCESS_CONTROL.address],
|
|
2531
|
+
},
|
|
2532
|
+
},
|
|
2533
|
+
redemptionInfo: {
|
|
2534
|
+
instantWithdrawalVault: InstantWithdrawalVault.YES,
|
|
2535
|
+
redemptionsInfo: [],
|
|
2536
|
+
alerts: [],
|
|
2537
|
+
},
|
|
2538
|
+
usualTimeToEarnings: null,
|
|
2539
|
+
usualTimeToEarningsDescription: null,
|
|
2105
2540
|
};
|
|
2106
2541
|
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "WBTC")!,
|
|
2125
|
-
lstContract: Global.getDefaultTokens().find((t) => t.symbol === "xWBTC")!.address,
|
|
2542
|
+
// Helper to create common LST alerts
|
|
2543
|
+
const getLSTAlerts = () => [
|
|
2544
|
+
{
|
|
2545
|
+
tab: "deposit" as const,
|
|
2546
|
+
text: (
|
|
2547
|
+
<>
|
|
2548
|
+
To acquire the LST, please visit{" "}
|
|
2549
|
+
<a
|
|
2550
|
+
href="https://app.endur.fi"
|
|
2551
|
+
target="_blank"
|
|
2552
|
+
rel="noopener noreferrer"
|
|
2553
|
+
>
|
|
2554
|
+
endur.fi
|
|
2555
|
+
</a>
|
|
2556
|
+
</>
|
|
2557
|
+
),
|
|
2558
|
+
type: "info" as const
|
|
2126
2559
|
},
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
additionalInfo: {
|
|
2146
|
-
...xSTRKSTRK.additionalInfo,
|
|
2147
|
-
quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "tBTC")!,
|
|
2148
|
-
lstContract: Global.getDefaultTokens().find((t) => t.symbol === "xtBTC")!.address,
|
|
2560
|
+
{
|
|
2561
|
+
type: "info" as const,
|
|
2562
|
+
text: (
|
|
2563
|
+
<p>
|
|
2564
|
+
Depending on the current position range and price, your input
|
|
2565
|
+
amounts are automatically adjusted to nearest required amounts.
|
|
2566
|
+
If you have insufficient tokens, you can acquire the required
|
|
2567
|
+
tokens on{" "}
|
|
2568
|
+
<a
|
|
2569
|
+
href="https://avnu.fi"
|
|
2570
|
+
target="_blank"
|
|
2571
|
+
rel="noopener noreferrer"
|
|
2572
|
+
>
|
|
2573
|
+
Avnu
|
|
2574
|
+
</a>
|
|
2575
|
+
</p>
|
|
2576
|
+
),
|
|
2577
|
+
tab: "deposit" as const
|
|
2149
2578
|
},
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2579
|
+
{
|
|
2580
|
+
type: "info" as const,
|
|
2581
|
+
text: (
|
|
2582
|
+
<>
|
|
2583
|
+
Depending on the current position range and price, you may
|
|
2584
|
+
receive both of the tokens or one of the tokens depending on the
|
|
2585
|
+
price
|
|
2586
|
+
</>
|
|
2587
|
+
),
|
|
2588
|
+
tab: "withdraw" as const
|
|
2589
|
+
}
|
|
2590
|
+
];
|
|
2591
|
+
|
|
2592
|
+
// Helper to create LST strategy settings
|
|
2593
|
+
const createLSTSettings = (quoteTokenSymbol: string) => ({
|
|
2594
|
+
...xSTRKSTRK.settings,
|
|
2595
|
+
isAudited: true,
|
|
2596
|
+
liveStatus: StrategyLiveStatus.ACTIVE,
|
|
2597
|
+
isInstantWithdrawal: true,
|
|
2598
|
+
hideNetEarnings: true,
|
|
2599
|
+
isTransactionHistDisabled: true,
|
|
2600
|
+
quoteToken: Global.getDefaultTokens().find(
|
|
2601
|
+
(t) => t.symbol === quoteTokenSymbol
|
|
2602
|
+
)!,
|
|
2603
|
+
alerts: getLSTAlerts(),
|
|
2604
|
+
tags: [StrategyTag.AUTOMATED_LP] as StrategyTag[],
|
|
2605
|
+
});
|
|
2606
|
+
|
|
2607
|
+
// Helper to create an LST strategy
|
|
2608
|
+
const createLSTStrategy = (params: {
|
|
2609
|
+
id: string;
|
|
2610
|
+
name: string;
|
|
2611
|
+
address: string;
|
|
2612
|
+
launchBlock: number;
|
|
2613
|
+
depositToken0Symbol: string;
|
|
2614
|
+
depositToken1Symbol: string;
|
|
2615
|
+
quoteTokenSymbol: string;
|
|
2616
|
+
lstSymbol: string;
|
|
2617
|
+
lstContractAddress?: string;
|
|
2618
|
+
}): IStrategyMetadata<CLVaultStrategySettings> => ({
|
|
2156
2619
|
...xSTRKSTRK,
|
|
2157
|
-
|
|
2620
|
+
id: params.id,
|
|
2621
|
+
name: params.name,
|
|
2158
2622
|
description: <></>,
|
|
2159
|
-
address: ContractAddr.from(
|
|
2160
|
-
"0x3af1c7faa7c464cf2c494e988972ad1939f1103dbfb6e47e9bf0c47e49b14ef"
|
|
2161
|
-
),
|
|
2162
|
-
launchBlock: 2344809,
|
|
2163
2623
|
// must be same order as poolKey token0 and token1
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
...xSTRKSTRK.additionalInfo,
|
|
2170
|
-
quoteAsset: Global.getDefaultTokens().find((t) => t.symbol === "solvBTC")!,
|
|
2171
|
-
lstContract: Global.getDefaultTokens().find((t) => t.symbol === "xsBTC")!.address,
|
|
2624
|
+
address: ContractAddr.from(params.address),
|
|
2625
|
+
launchBlock: params.launchBlock,
|
|
2626
|
+
vaultType: {
|
|
2627
|
+
type: vaultType,
|
|
2628
|
+
description: vaultTypeDescription
|
|
2172
2629
|
},
|
|
2173
|
-
faqs: getLSTFAQs("xsBTC"),
|
|
2174
|
-
points: [],
|
|
2175
|
-
contractDetails: [],
|
|
2176
|
-
investmentSteps: []
|
|
2177
|
-
},
|
|
2178
|
-
{
|
|
2179
|
-
...xSTRKSTRK,
|
|
2180
|
-
name: "Ekubo xLBTC/LBTC",
|
|
2181
|
-
description: <></>,
|
|
2182
|
-
address: ContractAddr.from(
|
|
2183
|
-
"0x314c4653ab1aa01f5465773cb879f525d7e369a137bc3ae084761aee99a1712"
|
|
2184
|
-
),
|
|
2185
|
-
launchBlock: 2412442,
|
|
2186
|
-
// must be same order as poolKey token0 and token1
|
|
2187
2630
|
depositTokens: [
|
|
2188
|
-
|
|
2189
|
-
|
|
2631
|
+
Global.getDefaultTokens().find(
|
|
2632
|
+
(t) => t.symbol === params.depositToken0Symbol
|
|
2633
|
+
)!,
|
|
2634
|
+
Global.getDefaultTokens().find((t) => t.symbol === params.depositToken1Symbol)!
|
|
2190
2635
|
],
|
|
2636
|
+
realizedApyMethodology: "The realizedAPY is based on past 14 days performance by the vault",
|
|
2191
2637
|
additionalInfo: {
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2638
|
+
...xSTRKSTRK.additionalInfo,
|
|
2639
|
+
quoteAsset: Global.getDefaultTokens().find(
|
|
2640
|
+
(t) => t.symbol === params.quoteTokenSymbol
|
|
2641
|
+
)!,
|
|
2642
|
+
lstContract: params.lstContractAddress
|
|
2643
|
+
? ContractAddr.from(params.lstContractAddress)
|
|
2644
|
+
: Global.getDefaultTokens().find((t) => t.symbol === params.lstSymbol)!
|
|
2645
|
+
.address
|
|
2195
2646
|
},
|
|
2196
|
-
|
|
2647
|
+
settings: createLSTSettings(params.quoteTokenSymbol),
|
|
2648
|
+
faqs: getLSTFAQs(params.lstSymbol),
|
|
2197
2649
|
points: [],
|
|
2198
2650
|
contractDetails: [],
|
|
2199
|
-
investmentSteps: []
|
|
2200
|
-
|
|
2651
|
+
investmentSteps: [],
|
|
2652
|
+
tags: params.id.toLowerCase().includes('btc') ? [StrategyTag.BTC, StrategyTag.AUTOMATED_LP] : [StrategyTag.AUTOMATED_LP]
|
|
2653
|
+
});
|
|
2654
|
+
|
|
2655
|
+
const lstStrategies: IStrategyMetadata<CLVaultStrategySettings>[] = [
|
|
2656
|
+
xSTRKSTRK,
|
|
2657
|
+
createLSTStrategy({
|
|
2658
|
+
id: "ekubo_cl_xstrkbtcstrkbtc",
|
|
2659
|
+
name: "Ekubo xstrkBTC/strkBTC",
|
|
2660
|
+
address: "0x03d1d1932ef6882d4acf763dd0430f4abed3e2a9da28e028f1e2e8dd934b8bf7",
|
|
2661
|
+
launchBlock: 9651140,
|
|
2662
|
+
depositToken0Symbol: "xstrkBTC",
|
|
2663
|
+
depositToken1Symbol: "strkBTC",
|
|
2664
|
+
quoteTokenSymbol: "strkBTC",
|
|
2665
|
+
lstSymbol: "xstrkBTC",
|
|
2666
|
+
}),
|
|
2667
|
+
createLSTStrategy({
|
|
2668
|
+
id: "ekubo_cl_xwbtcwbtc",
|
|
2669
|
+
name: "Ekubo xWBTC/WBTC",
|
|
2670
|
+
address: "0x2ea99b4971d3c277fa4a9b4beb7d4d7d169e683393a29eef263d5d57b4380a",
|
|
2671
|
+
launchBlock: 2338309,
|
|
2672
|
+
depositToken0Symbol: "WBTC",
|
|
2673
|
+
depositToken1Symbol: "xWBTC",
|
|
2674
|
+
quoteTokenSymbol: "WBTC",
|
|
2675
|
+
lstSymbol: "xWBTC",
|
|
2676
|
+
}),
|
|
2677
|
+
createLSTStrategy({
|
|
2678
|
+
id: "ekubo_cl_xtbtctbtc",
|
|
2679
|
+
name: "Ekubo xtBTC/tBTC",
|
|
2680
|
+
address: "0x785dc3dfc4e80ef2690a99512481e3ed3a5266180adda5a47e856245d68a4af",
|
|
2681
|
+
launchBlock: 2415667,
|
|
2682
|
+
depositToken0Symbol: "xtBTC",
|
|
2683
|
+
depositToken1Symbol: "tBTC",
|
|
2684
|
+
quoteTokenSymbol: "tBTC",
|
|
2685
|
+
lstSymbol: "xtBTC",
|
|
2686
|
+
}),
|
|
2687
|
+
createLSTStrategy({
|
|
2688
|
+
id: "ekubo_cl_xsbtcsolvbtc",
|
|
2689
|
+
name: "Ekubo xsBTC/solvBTC",
|
|
2690
|
+
address: "0x3af1c7faa7c464cf2c494e988972ad1939f1103dbfb6e47e9bf0c47e49b14ef",
|
|
2691
|
+
launchBlock: 2344809,
|
|
2692
|
+
depositToken0Symbol: "xsBTC",
|
|
2693
|
+
depositToken1Symbol: "solvBTC",
|
|
2694
|
+
quoteTokenSymbol: "solvBTC",
|
|
2695
|
+
lstSymbol: "xsBTC",
|
|
2696
|
+
}),
|
|
2697
|
+
createLSTStrategy({
|
|
2698
|
+
id: "ekubo_cl_xlbtclbtc",
|
|
2699
|
+
name: "Ekubo xLBTC/LBTC",
|
|
2700
|
+
address: "0x314c4653ab1aa01f5465773cb879f525d7e369a137bc3ae084761aee99a1712",
|
|
2701
|
+
launchBlock: 2412442,
|
|
2702
|
+
depositToken0Symbol: "LBTC",
|
|
2703
|
+
depositToken1Symbol: "xLBTC",
|
|
2704
|
+
quoteTokenSymbol: "LBTC",
|
|
2705
|
+
lstSymbol: "xLBTC",
|
|
2706
|
+
})
|
|
2201
2707
|
];
|
|
2202
2708
|
|
|
2203
|
-
const
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
rebalanceConditions: {
|
|
2223
|
-
customShouldRebalance: async (currentPrice: number) =>
|
|
2224
|
-
currentPrice > 0.99 && currentPrice < 1.01,
|
|
2225
|
-
minWaitHours: 6,
|
|
2226
|
-
direction: "any"
|
|
2709
|
+
const getRe7Alerts = () => [
|
|
2710
|
+
{
|
|
2711
|
+
type: "info" as const,
|
|
2712
|
+
text: (
|
|
2713
|
+
<p>
|
|
2714
|
+
Depending on the current position range and price, your input
|
|
2715
|
+
amounts are automatically adjusted to nearest required amounts.
|
|
2716
|
+
If you have insufficient tokens, you can acquire the required
|
|
2717
|
+
tokens on{" "}
|
|
2718
|
+
<a
|
|
2719
|
+
href="https://avnu.fi"
|
|
2720
|
+
target="_blank"
|
|
2721
|
+
rel="noopener noreferrer"
|
|
2722
|
+
>
|
|
2723
|
+
Avnu
|
|
2724
|
+
</a>
|
|
2725
|
+
</p>
|
|
2726
|
+
),
|
|
2727
|
+
tab: "deposit" as const
|
|
2227
2728
|
},
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2729
|
+
{
|
|
2730
|
+
type: "info" as const,
|
|
2731
|
+
text: (
|
|
2732
|
+
<>
|
|
2733
|
+
Depending on the current position range and price, you may
|
|
2734
|
+
receive both of the tokens or one of the tokens depending on the
|
|
2735
|
+
price
|
|
2736
|
+
</>
|
|
2737
|
+
),
|
|
2738
|
+
tab: "withdraw" as const
|
|
2739
|
+
}
|
|
2740
|
+
];
|
|
2741
|
+
|
|
2742
|
+
// Helper to create Re7 strategy settings
|
|
2743
|
+
const createRe7Settings = (quoteTokenSymbol: string, isBTC: boolean, isDeprecated: boolean) => ({
|
|
2744
|
+
...xSTRKSTRK.settings,
|
|
2745
|
+
isAudited: true,
|
|
2746
|
+
liveStatus: isDeprecated ? StrategyLiveStatus.DEPRECATED : StrategyLiveStatus.ACTIVE,
|
|
2747
|
+
isInstantWithdrawal: true,
|
|
2748
|
+
hideNetEarnings: true,
|
|
2749
|
+
isTransactionHistDisabled: false,
|
|
2750
|
+
quoteToken: Global.getDefaultTokens().find(
|
|
2751
|
+
(t) => t.symbol === quoteTokenSymbol
|
|
2752
|
+
)!,
|
|
2753
|
+
alerts: getRe7Alerts(),
|
|
2754
|
+
tags: isBTC ? [StrategyTag.BTC, StrategyTag.AUTOMATED_LP] : [StrategyTag.AUTOMATED_LP] as StrategyTag[]
|
|
2755
|
+
});
|
|
2756
|
+
|
|
2757
|
+
// Helper to create Re7 FAQs
|
|
2758
|
+
const getRe7FAQs = () => [
|
|
2231
2759
|
...faqs,
|
|
2232
2760
|
{
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2761
|
+
question: "Who is the curator of this strategy?",
|
|
2762
|
+
answer: (
|
|
2763
|
+
<div>
|
|
2764
|
+
Re7 Labs is the curator of this strategy. Re7 Labs is a
|
|
2765
|
+
well-known Web3 asset management firm. This strategy is
|
|
2766
|
+
completely managed by them, including ownership of the vault.
|
|
2767
|
+
Troves is developer of the smart contracts and maintains
|
|
2768
|
+
infrastructure to help users access these strategies. You can
|
|
2769
|
+
find more information about them on their website{" "}
|
|
2770
|
+
<a
|
|
2771
|
+
href="https://www.re7labs.xyz"
|
|
2772
|
+
style={{
|
|
2773
|
+
textDecoration: "underline",
|
|
2774
|
+
marginLeft: "2px"
|
|
2775
|
+
}}
|
|
2776
|
+
target="_blank"
|
|
2777
|
+
>
|
|
2778
|
+
here
|
|
2779
|
+
</a>
|
|
2780
|
+
.
|
|
2781
|
+
</div>
|
|
2782
|
+
)
|
|
2236
2783
|
},
|
|
2237
2784
|
{
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2785
|
+
question: "How is the APY calculated?",
|
|
2786
|
+
answer: (
|
|
2787
|
+
<div>
|
|
2788
|
+
It's an annualized fee APY, calculated as fees earned in the
|
|
2789
|
+
last 24h divided by TVL. Factors like impermanent loss are not
|
|
2790
|
+
considered.
|
|
2791
|
+
</div>
|
|
2792
|
+
)
|
|
2793
|
+
}
|
|
2794
|
+
];
|
|
2795
|
+
|
|
2796
|
+
// Helper to create a Re7 strategy
|
|
2797
|
+
const createRe7Strategy = (
|
|
2798
|
+
id: string,
|
|
2799
|
+
name: string,
|
|
2800
|
+
address: string,
|
|
2801
|
+
launchBlock: number,
|
|
2802
|
+
depositToken0Symbol: string,
|
|
2803
|
+
depositToken1Symbol: string,
|
|
2804
|
+
quoteTokenSymbol: string,
|
|
2805
|
+
risk:
|
|
2806
|
+
| typeof highRisk
|
|
2807
|
+
| typeof mediumRisk
|
|
2808
|
+
| { riskFactor: RiskFactor[]; netRisk: number; notARisks: RiskType[] },
|
|
2809
|
+
isBTC: boolean
|
|
2810
|
+
): IStrategyMetadata<CLVaultStrategySettings> => {
|
|
2811
|
+
const isDeprecated = name.toLowerCase().includes('usdc.e');
|
|
2812
|
+
return {
|
|
2813
|
+
...xSTRKSTRK,
|
|
2814
|
+
id,
|
|
2815
|
+
name,
|
|
2816
|
+
description: <></>,
|
|
2817
|
+
address: ContractAddr.from(address),
|
|
2818
|
+
launchBlock,
|
|
2819
|
+
vaultType: {
|
|
2820
|
+
type: vaultType,
|
|
2821
|
+
description: vaultTypeDescription
|
|
2822
|
+
},
|
|
2823
|
+
depositTokens: [
|
|
2824
|
+
Global.getDefaultTokens().find(
|
|
2825
|
+
(t) => t.symbol === depositToken0Symbol
|
|
2826
|
+
)!,
|
|
2827
|
+
Global.getDefaultTokens().find((t) => t.symbol === depositToken1Symbol)!
|
|
2828
|
+
],
|
|
2829
|
+
apyMethodology:
|
|
2830
|
+
"Annualized fee APY, calculated as fees earned in the last 7d divided by TVL",
|
|
2831
|
+
additionalInfo: {
|
|
2832
|
+
newBounds: "Managed by Re7",
|
|
2833
|
+
truePrice: 1,
|
|
2834
|
+
feeBps: 1000,
|
|
2835
|
+
rebalanceConditions: {
|
|
2836
|
+
customShouldRebalance: async (currentPrice: number) =>
|
|
2837
|
+
currentPrice > 0.99 && currentPrice < 1.01,
|
|
2838
|
+
minWaitHours: 6,
|
|
2839
|
+
direction: "any" as const
|
|
2840
|
+
},
|
|
2841
|
+
quoteAsset: Global.getDefaultTokens().find(
|
|
2842
|
+
(t) => t.symbol === quoteTokenSymbol
|
|
2843
|
+
)!
|
|
2844
|
+
},
|
|
2845
|
+
settings: createRe7Settings(quoteTokenSymbol, isBTC, isDeprecated),
|
|
2846
|
+
faqs: getRe7FAQs(),
|
|
2847
|
+
risk,
|
|
2848
|
+
points: [],
|
|
2849
|
+
curator: { name: "Re7 Labs", logo: "https://www.re7labs.xyz/favicon.ico" },
|
|
2850
|
+
tags: isBTC ? [StrategyTag.BTC, StrategyTag.AUTOMATED_LP] : [StrategyTag.AUTOMATED_LP] as StrategyTag[],
|
|
2851
|
+
discontinuationInfo: isDeprecated ? {
|
|
2852
|
+
info: "This strategy has been deprecated and is no longer accepting new deposits."
|
|
2853
|
+
} : undefined,
|
|
2854
|
+
security: {
|
|
2855
|
+
...xSTRKSTRK.security,
|
|
2856
|
+
accessControl: {
|
|
2857
|
+
...xSTRKSTRK.security.accessControl,
|
|
2858
|
+
addresses: [ContractAddr.from("0x707bf89863473548fb2844c9f3f96d83fe2394453259035a5791e4b1490642")],
|
|
2859
|
+
},
|
|
2860
|
+
},
|
|
2861
|
+
};
|
|
2862
|
+
};
|
|
2863
|
+
|
|
2864
|
+
const ETHUSDCRe7Strategy = createRe7Strategy(
|
|
2865
|
+
"ekubo_cl_ethusdc",
|
|
2866
|
+
"Ekubo ETH/USDC.e",
|
|
2867
|
+
"0x160d8fa4569ef6a12e6bf47cb943d7b5ebba8a41a69a14c1d943050ba5ff947",
|
|
2868
|
+
1504232,
|
|
2869
|
+
"ETH",
|
|
2870
|
+
"USDC.e",
|
|
2871
|
+
"USDC.e",
|
|
2872
|
+
highRisk,
|
|
2873
|
+
false // isBTC
|
|
2874
|
+
);
|
|
2875
|
+
|
|
2876
|
+
const stableCoinRisk = {
|
|
2877
|
+
riskFactor: _stableCoinPoolRiskFactors,
|
|
2878
|
+
netRisk:
|
|
2879
|
+
_stableCoinPoolRiskFactors.reduce(
|
|
2880
|
+
(acc, curr) => acc + curr.value * curr.weight,
|
|
2881
|
+
0
|
|
2882
|
+
) /
|
|
2883
|
+
_stableCoinPoolRiskFactors.reduce((acc, curr) => acc + curr.weight, 0),
|
|
2884
|
+
notARisks: getNoRiskTags(_stableCoinPoolRiskFactors)
|
|
2246
2885
|
};
|
|
2247
2886
|
|
|
2248
2887
|
const RE7Strategies: IStrategyMetadata<CLVaultStrategySettings>[] = [
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2888
|
+
ETHUSDCRe7Strategy,
|
|
2889
|
+
createRe7Strategy(
|
|
2890
|
+
"ekubo_cl_usdcusdt",
|
|
2891
|
+
"Ekubo USDC.e/USDT",
|
|
2892
|
+
"0x3a4f8debaf12af97bb911099bc011d63d6c208d4c5ba8e15d7f437785b0aaa2",
|
|
2893
|
+
1506139,
|
|
2894
|
+
"USDC.e",
|
|
2895
|
+
"USDT",
|
|
2896
|
+
"USDC.e",
|
|
2897
|
+
stableCoinRisk,
|
|
2898
|
+
false // isBTC
|
|
2256
2899
|
),
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
_stableCoinPoolRiskFactors.reduce((acc, curr) => acc + curr.weight, 0),
|
|
2268
|
-
notARisks: getNoRiskTags(_stableCoinPoolRiskFactors),
|
|
2269
|
-
}
|
|
2270
|
-
},
|
|
2271
|
-
{
|
|
2272
|
-
...ETHUSDCRe7Strategy,
|
|
2273
|
-
name: "Ekubo STRK/USDC",
|
|
2274
|
-
description: <></>,
|
|
2275
|
-
address: ContractAddr.from(
|
|
2276
|
-
"0x351b36d0d9d8b40010658825adeeddb1397436cd41acd0ff6c6e23aaa8b5b30"
|
|
2900
|
+
createRe7Strategy(
|
|
2901
|
+
"ekubo_cl_strkusdc",
|
|
2902
|
+
"Ekubo STRK/USDC.e",
|
|
2903
|
+
"0x351b36d0d9d8b40010658825adeeddb1397436cd41acd0ff6c6e23aaa8b5b30",
|
|
2904
|
+
1504079,
|
|
2905
|
+
"STRK",
|
|
2906
|
+
"USDC.e",
|
|
2907
|
+
"USDC.e",
|
|
2908
|
+
highRisk,
|
|
2909
|
+
false // isBTC
|
|
2277
2910
|
),
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
name: "Ekubo STRK/ETH",
|
|
2289
|
-
description: <></>,
|
|
2290
|
-
address: ContractAddr.from(
|
|
2291
|
-
"0x4ce3024b0ee879009112d7b0e073f8a87153dd35b029347d4247ffe48d28f51"
|
|
2911
|
+
createRe7Strategy(
|
|
2912
|
+
"ekubo_cl_strketh",
|
|
2913
|
+
"Ekubo STRK/ETH",
|
|
2914
|
+
"0x4ce3024b0ee879009112d7b0e073f8a87153dd35b029347d4247ffe48d28f51",
|
|
2915
|
+
1504149,
|
|
2916
|
+
"STRK",
|
|
2917
|
+
"ETH",
|
|
2918
|
+
"USDC",
|
|
2919
|
+
highRisk,
|
|
2920
|
+
false // isBTC
|
|
2292
2921
|
),
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
name: "Ekubo WBTC/USDC",
|
|
2304
|
-
description: <></>,
|
|
2305
|
-
address: ContractAddr.from(
|
|
2306
|
-
"0x2bcaef2eb7706875a5fdc6853dd961a0590f850bc3a031c59887189b5e84ba1"
|
|
2922
|
+
createRe7Strategy(
|
|
2923
|
+
"ekubo_cl_wbtcusdc",
|
|
2924
|
+
"Ekubo WBTC/USDC.e",
|
|
2925
|
+
"0x2bcaef2eb7706875a5fdc6853dd961a0590f850bc3a031c59887189b5e84ba1",
|
|
2926
|
+
1506144,
|
|
2927
|
+
"WBTC",
|
|
2928
|
+
"USDC.e",
|
|
2929
|
+
"USDC.e",
|
|
2930
|
+
mediumRisk,
|
|
2931
|
+
true // isBTC
|
|
2307
2932
|
),
|
|
2308
|
-
|
|
2309
|
-
//
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2933
|
+
// createRe7Strategy(
|
|
2934
|
+
// "ekubo_cl_tbtcusdce",
|
|
2935
|
+
// "Ekubo tBTC/USDC.e",
|
|
2936
|
+
// "0x4aad891a2d4432fba06b6558631bb13f6bbd7f6f33ab8c3111e344889ea4456",
|
|
2937
|
+
// 1501764,
|
|
2938
|
+
// "tBTC",
|
|
2939
|
+
// "USDC.e",
|
|
2940
|
+
// "USDC.e",
|
|
2941
|
+
// mediumRisk,
|
|
2942
|
+
// ),
|
|
2943
|
+
createRe7Strategy(
|
|
2944
|
+
"ekubo_cl_wbtceth",
|
|
2945
|
+
"Ekubo WBTC/ETH",
|
|
2946
|
+
"0x1c9232b8186d9317652f05055615f18a120c2ad9e5ee96c39e031c257fb945b",
|
|
2947
|
+
1506145,
|
|
2948
|
+
"WBTC",
|
|
2949
|
+
"ETH",
|
|
2950
|
+
"USDC",
|
|
2951
|
+
mediumRisk,
|
|
2952
|
+
true // isBTC
|
|
2322
2953
|
),
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
name: "Ekubo WBTC/ETH",
|
|
2334
|
-
description: <></>,
|
|
2335
|
-
address: ContractAddr.from(
|
|
2336
|
-
"0x1c9232b8186d9317652f05055615f18a120c2ad9e5ee96c39e031c257fb945b"
|
|
2954
|
+
createRe7Strategy(
|
|
2955
|
+
"ekubo_cl_wbtcstrk",
|
|
2956
|
+
"Ekubo WBTC/STRK",
|
|
2957
|
+
"0x1248e385c23a929a015ec298a26560fa7745bbd6e41a886550e337b02714b1b",
|
|
2958
|
+
1506147,
|
|
2959
|
+
"WBTC",
|
|
2960
|
+
"STRK",
|
|
2961
|
+
"USDC",
|
|
2962
|
+
highRisk,
|
|
2963
|
+
true // isBTC
|
|
2337
2964
|
),
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2965
|
+
createRe7Strategy(
|
|
2966
|
+
"ekubo_cl_usdc_v2usdt",
|
|
2967
|
+
"Ekubo USDC/USDT",
|
|
2968
|
+
"0x5203a08b471e46bf33990ac83aff577bbe5a5d789e61de2c6531e3c4773d1c9",
|
|
2969
|
+
3998018,
|
|
2970
|
+
"USDC",
|
|
2971
|
+
"USDT",
|
|
2972
|
+
"USDC",
|
|
2973
|
+
stableCoinRisk,
|
|
2974
|
+
false // isBTC
|
|
2975
|
+
),
|
|
2976
|
+
createRe7Strategy(
|
|
2977
|
+
"ekubo_cl_ethusdc_v2",
|
|
2978
|
+
"Ekubo ETH/USDC",
|
|
2979
|
+
"0x4d00c7423b3c0fae3640f6099ac97acbfd8708f099e09bfe3a7a6a680399228",
|
|
2980
|
+
3998025,
|
|
2981
|
+
"USDC",
|
|
2982
|
+
"ETH",
|
|
2983
|
+
"USDC",
|
|
2984
|
+
highRisk,
|
|
2985
|
+
false // isBTC
|
|
2986
|
+
),
|
|
2987
|
+
createRe7Strategy(
|
|
2988
|
+
"ekubo_cl_strkusdc_v2",
|
|
2989
|
+
"Ekubo STRK/USDC",
|
|
2990
|
+
"0x4de22bd0a8eb4d0a18736e66dd36d20ba50bc106346bbfac3dbeaac1ab37ce1",
|
|
2991
|
+
3998030,
|
|
2992
|
+
"USDC",
|
|
2993
|
+
"STRK",
|
|
2994
|
+
"USDC",
|
|
2995
|
+
highRisk,
|
|
2996
|
+
false // isBTC
|
|
2997
|
+
),
|
|
2998
|
+
createRe7Strategy(
|
|
2999
|
+
"ekubo_cl_wbtcusdc_v2",
|
|
3000
|
+
"Ekubo WBTC/USDC",
|
|
3001
|
+
"0x76101c3b80af1103c9c6d541ca627f61b5ae7ae79d7fce96ccdf7bdb648450d",
|
|
3002
|
+
3998034,
|
|
3003
|
+
"USDC",
|
|
3004
|
+
"WBTC",
|
|
3005
|
+
"USDC",
|
|
3006
|
+
mediumRisk,
|
|
3007
|
+
true // isBTC
|
|
3008
|
+
),
|
|
3009
|
+
createRe7Strategy(
|
|
3010
|
+
"ekubo_cl_strkbtcusdc",
|
|
3011
|
+
"Ekubo strkBTC/USDC",
|
|
3012
|
+
"0x02dfe5af1665a7adf549008161c818eb18dcf89fc9518ab812294f2b691b2845",
|
|
3013
|
+
9650986,
|
|
3014
|
+
"USDC",
|
|
3015
|
+
"strkBTC",
|
|
3016
|
+
"USDC",
|
|
3017
|
+
mediumRisk,
|
|
3018
|
+
true // isBTC
|
|
3019
|
+
),
|
|
3020
|
+
createRe7Strategy(
|
|
3021
|
+
"ekubo_cl_strkbtcstrk",
|
|
3022
|
+
"Ekubo strkBTC/STRK",
|
|
3023
|
+
"0x04784e62a4847484528ba65f500b37a9347e88632e90d866e213f2c2651be828",
|
|
3024
|
+
9650592,
|
|
3025
|
+
"STRK",
|
|
3026
|
+
"strkBTC",
|
|
3027
|
+
"USDC",
|
|
3028
|
+
mediumRisk,
|
|
3029
|
+
true // isBTC
|
|
3030
|
+
),
|
|
3031
|
+
createRe7Strategy(
|
|
3032
|
+
"ekubo_cl_strkbtceth",
|
|
3033
|
+
"Ekubo strkBTC/ETH",
|
|
3034
|
+
"0x07118ecd7dece83462b0ac8302c682fb17c7e18b0be13d81867c5bf3f80933ef",
|
|
3035
|
+
9650986,
|
|
3036
|
+
"ETH",
|
|
3037
|
+
"strkBTC",
|
|
3038
|
+
"USDC",
|
|
3039
|
+
mediumRisk,
|
|
3040
|
+
true // isBTC
|
|
3041
|
+
),
|
|
3042
|
+
// wbtc/strkBTC
|
|
3043
|
+
createRe7Strategy(
|
|
3044
|
+
"ekubo_cl_wbtcstrkbtc",
|
|
3045
|
+
"Ekubo WBTC/strkBTC",
|
|
3046
|
+
"0x07e927222730899442b2438bfd6218ff8ac44bd7a3420646fca359b8392e42c1",
|
|
3047
|
+
9650986,
|
|
3048
|
+
"WBTC",
|
|
3049
|
+
"strkBTC",
|
|
3050
|
+
"strkBTC",
|
|
3051
|
+
stableCoinRisk,
|
|
3052
|
+
true // isBTC
|
|
2352
3053
|
),
|
|
2353
|
-
launchBlock: 1506147,
|
|
2354
|
-
// must be same order as poolKey token0 and token1
|
|
2355
|
-
depositTokens: [
|
|
2356
|
-
Global.getDefaultTokens().find((t) => t.symbol === "WBTC")!,
|
|
2357
|
-
Global.getDefaultTokens().find((t) => t.symbol === "STRK")!
|
|
2358
|
-
],
|
|
2359
|
-
risk: highRisk,
|
|
2360
|
-
}
|
|
2361
3054
|
];
|
|
2362
3055
|
|
|
2363
3056
|
/**
|
|
@@ -2375,7 +3068,7 @@ EkuboCLVaultStrategies.forEach((s) => {
|
|
|
2375
3068
|
address: s.address,
|
|
2376
3069
|
name: "Vault",
|
|
2377
3070
|
sourceCodeUrl: "https://github.com/strkfarm/strkfarm-contracts/tree/main/src/strategies/cl_vault"
|
|
2378
|
-
},
|
|
3071
|
+
},
|
|
2379
3072
|
// ...COMMON_CONTRACTS
|
|
2380
3073
|
];
|
|
2381
3074
|
// set docs link
|
|
@@ -2419,4 +3112,4 @@ EkuboCLVaultStrategies.forEach((s) => {
|
|
|
2419
3112
|
"Monitor and Rebalance position to optimize yield",
|
|
2420
3113
|
"Harvest and re-invest any rewards every week (Auto-compound)",
|
|
2421
3114
|
]
|
|
2422
|
-
});
|
|
3115
|
+
});
|