@strkfarm/sdk 2.0.0-dev.43 → 2.0.0-dev.45
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 +2 -2
- package/dist/cli.mjs +2 -2
- package/dist/index.browser.global.js +26404 -25513
- package/dist/index.browser.mjs +854 -559
- package/dist/index.d.ts +51 -6
- package/dist/index.js +889 -591
- package/dist/index.mjs +859 -562
- package/package.json +72 -14
- package/src/global.ts +219 -230
- package/src/interfaces/common.tsx +10 -0
- package/src/modules/ekubo-pricer.ts +2 -1
- package/src/modules/pricer.ts +27 -15
- package/src/modules/zkLend.ts +3 -2
- package/src/strategies/universal-lst-muliplier-strategy.tsx +294 -20
- package/src/strategies/yoloVault.ts +2 -0
package/src/modules/pricer.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import axios from "axios";
|
|
2
2
|
import { FatalError, Global } from "@/global";
|
|
3
|
-
import { TokenInfo } from "@/interfaces/common";
|
|
3
|
+
import { PriceMethod, TokenInfo } from "@/interfaces/common";
|
|
4
4
|
import { IConfig } from "@/interfaces/common";
|
|
5
5
|
import { Web3Number } from "@/dataTypes";
|
|
6
6
|
import { PricerBase } from "./pricerBase";
|
|
@@ -14,8 +14,6 @@ export interface PriceInfo {
|
|
|
14
14
|
timestamp: Date
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
type PriceMethod = 'AvnuApi' | 'Coinbase' | 'Coinmarketcap' | 'Ekubo' | 'Avnu';
|
|
18
|
-
|
|
19
17
|
const PRICE_METHOD_PRIORITY: PriceMethod[] = [
|
|
20
18
|
'AvnuApi',
|
|
21
19
|
'Coinbase',
|
|
@@ -164,22 +162,15 @@ export class Pricer extends PricerBase {
|
|
|
164
162
|
}
|
|
165
163
|
|
|
166
164
|
async _getPrice(token: TokenInfo): Promise<number> {
|
|
167
|
-
const
|
|
168
|
-
if (pinned) {
|
|
169
|
-
logger.verbose(`Fetching price of ${token.symbol} using pinned ${pinned}`);
|
|
170
|
-
try {
|
|
171
|
-
return await this._tryPriceMethod(token, pinned);
|
|
172
|
-
} catch (error: any) {
|
|
173
|
-
console.warn(`${pinned}: pinned price failed [${token.symbol}]: `, error.message);
|
|
174
|
-
delete this.methodToUse[token.symbol];
|
|
175
|
-
}
|
|
176
|
-
}
|
|
165
|
+
const methodsToTry = this._getMethodsToTry(token);
|
|
177
166
|
|
|
178
|
-
for (const method of
|
|
167
|
+
for (const method of methodsToTry) {
|
|
179
168
|
logger.verbose(`Fetching price of ${token.symbol} using ${method}`);
|
|
180
169
|
try {
|
|
181
170
|
const result = await this._tryPriceMethod(token, method);
|
|
182
|
-
|
|
171
|
+
if (!token.priceMethod) {
|
|
172
|
+
this.methodToUse[token.symbol] = method;
|
|
173
|
+
}
|
|
183
174
|
return result;
|
|
184
175
|
} catch (error: any) {
|
|
185
176
|
console.warn(`${method}: price err [${token.symbol}]: `, error.message);
|
|
@@ -189,6 +180,27 @@ export class Pricer extends PricerBase {
|
|
|
189
180
|
throw new FatalError(`Price not found for ${token.symbol}`);
|
|
190
181
|
}
|
|
191
182
|
|
|
183
|
+
protected _getMethodsToTry(token: TokenInfo): PriceMethod[] {
|
|
184
|
+
const methods: PriceMethod[] = [];
|
|
185
|
+
|
|
186
|
+
if (token.priceMethod) {
|
|
187
|
+
methods.push(token.priceMethod);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const pinned = this.methodToUse[token.symbol];
|
|
191
|
+
if (pinned && pinned !== token.priceMethod) {
|
|
192
|
+
methods.push(pinned);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (const method of PRICE_METHOD_PRIORITY) {
|
|
196
|
+
if (!methods.includes(method)) {
|
|
197
|
+
methods.push(method);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return methods;
|
|
202
|
+
}
|
|
203
|
+
|
|
192
204
|
protected async _tryPriceMethod(token: TokenInfo, method: PriceMethod): Promise<number> {
|
|
193
205
|
switch (method) {
|
|
194
206
|
case 'AvnuApi':
|
package/src/modules/zkLend.ts
CHANGED
|
@@ -2,7 +2,7 @@ import axios from "axios";
|
|
|
2
2
|
import BigNumber from "bignumber.js";
|
|
3
3
|
import { Web3Number } from "@/dataTypes/bignumber.browser";
|
|
4
4
|
import { FatalError, Global } from "@/global";
|
|
5
|
-
import { TokenInfo } from "@/interfaces";
|
|
5
|
+
import { TokenIndexingType, TokenInfo } from "@/interfaces";
|
|
6
6
|
import { ILending, ILendingPosition, LendingToken, MarginType } from "@/interfaces/lending";
|
|
7
7
|
import { ContractAddr } from "@/dataTypes/address";
|
|
8
8
|
import { IConfig } from "@/interfaces";
|
|
@@ -42,7 +42,8 @@ export class ZkLend extends ILending implements ILending {
|
|
|
42
42
|
decimals: pool.token.decimals,
|
|
43
43
|
borrowFactor: Web3Number.fromWei(pool.borrow_factor.value, pool.borrow_factor.decimals),
|
|
44
44
|
collareralFactor,
|
|
45
|
-
displayDecimals: 2
|
|
45
|
+
displayDecimals: 2,
|
|
46
|
+
indexingType: TokenIndexingType.IGNORE,
|
|
46
47
|
}
|
|
47
48
|
this.tokens.push(token);
|
|
48
49
|
});
|
|
@@ -4,13 +4,15 @@ import { PricerBase } from "@/modules/pricerBase";
|
|
|
4
4
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
5
5
|
import { Global } from "@/global";
|
|
6
6
|
import { ApproveCallParams, APYType, AvnuSwapCallParams, BaseAdapterConfig, CommonAdapter, ManageCall, PositionInfo, TokenTransferAdapter, VesuModifyPositionAdapter, VesuMultiplyAdapter, VesuPools, VesuSupplyOnlyAdapter } from "./universal-adapters";
|
|
7
|
+
import { VesuAdapter } from "./universal-adapters/vesu-adapter";
|
|
7
8
|
import { AVNU_EXCHANGE } from "./universal-adapters/adapter-utils";
|
|
8
9
|
import { DepegRiskLevel, LiquidationRiskLevel, SmartContractRiskLevel, TechnicalRiskLevel } from "@/interfaces/risks";
|
|
9
10
|
import { AvnuWrapper, EkuboQuoter, ERC20, LSTAPRService, PricerFromApi, PricerLST } from "@/modules";
|
|
10
11
|
import { assert, logger } from "@/utils";
|
|
12
|
+
import { findMaxInputWithSlippage } from "@/utils/math-utils";
|
|
11
13
|
import { SingleActionAmount, SingleTokenInfo, UserPositionCard, UserPositionCardsInput } from "./base-strategy";
|
|
12
14
|
import { SVKStrategy } from "./svk-strategy";
|
|
13
|
-
import { Call, Contract, uint256 } from "starknet";
|
|
15
|
+
import { Call, Contract, num, uint256, BlockIdentifier } from "starknet";
|
|
14
16
|
import ERC4626Abi from "@/data/erc4626.abi.json";
|
|
15
17
|
import { AdapterOptimizer } from "./universal-adapters/adapter-optimizer";
|
|
16
18
|
import { LSTPriceType } from "./types";
|
|
@@ -544,48 +546,318 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
544
546
|
splits: { apy: number; id: string }[];
|
|
545
547
|
}> {
|
|
546
548
|
const unusedBalance = await this.getUnusedBalance();
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
+
const maxNewDeposits = await this.maxNewDeposits({
|
|
550
|
+
isAPYComputation: true,
|
|
551
|
+
});
|
|
552
|
+
const lstAPY = await this.getLSTAPR(
|
|
553
|
+
this.getLSTUnderlyingTokenInfo().address,
|
|
554
|
+
);
|
|
549
555
|
|
|
550
|
-
if
|
|
556
|
+
// if unused balance is > max servicable from loan, we are limited by the max borrowing we can do
|
|
557
|
+
// we also allow accepting little higher deposits (1.5x) to have room for future looping when theres more liquidity or debt cap rises
|
|
558
|
+
if (maxNewDeposits * 1.5 < unusedBalance.amount.toNumber()) {
|
|
559
|
+
// we have excess, just use real APY
|
|
551
560
|
logger.verbose(
|
|
552
561
|
`${this.getTag()}::netAPY: unused balance is > max servicable from loan, lstAPY: ${lstAPY}`,
|
|
553
562
|
);
|
|
554
|
-
|
|
555
|
-
const
|
|
563
|
+
const { positions, baseAPYs, rewardAPYs } = await this.getVesuAPYs();
|
|
564
|
+
const unusedBalanceAPY = await this.getUnusedBalanceAPY();
|
|
565
|
+
baseAPYs.push(...[unusedBalanceAPY.apy]);
|
|
566
|
+
rewardAPYs.push(0);
|
|
567
|
+
|
|
568
|
+
const weights = positions.map((p: VaultPosition, index: number) => p.usdValue * (index % 2 == 0 ? 1 : -1));
|
|
569
|
+
weights.push(unusedBalanceAPY.weight);
|
|
570
|
+
|
|
571
|
+
const prevAUM = await this.getPrevAUM();
|
|
572
|
+
const price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
|
|
573
|
+
const prevAUMUSD = prevAUM.multipliedBy(price.price);
|
|
574
|
+
const output = await this.returnNetAPY(baseAPYs, rewardAPYs, weights, prevAUMUSD);
|
|
556
575
|
output.splits.push({ apy: lstAPY, id: "lst_apy" });
|
|
557
576
|
return output;
|
|
558
577
|
} else {
|
|
578
|
+
// we have little bit room to accept more deposits, we use theoretical max APY
|
|
559
579
|
logger.verbose(
|
|
560
580
|
`${this.getTag()}::netAPY: we can take more deposits, use theoretical max APY`,
|
|
561
581
|
);
|
|
562
|
-
const
|
|
582
|
+
const { positions, baseAPYs, rewardAPYs } = await this.getVesuAPYs();
|
|
583
|
+
const weights = positions.map(
|
|
584
|
+
(p: VaultPosition, index: number) => p.usdValue * (index % 2 == 0 ? 1 : -1),
|
|
585
|
+
);
|
|
586
|
+
const aum = weights.reduce((acc: number, curr: number) => acc + curr, 0);
|
|
587
|
+
const output = await this.returnNetAPY(
|
|
588
|
+
baseAPYs,
|
|
589
|
+
rewardAPYs,
|
|
590
|
+
weights,
|
|
591
|
+
new Web3Number(
|
|
592
|
+
aum.toFixed(9),
|
|
593
|
+
this.getLSTUnderlyingTokenInfo().decimals,
|
|
594
|
+
),
|
|
595
|
+
);
|
|
563
596
|
output.splits.push({ apy: lstAPY, id: "lst_apy" });
|
|
564
597
|
return output;
|
|
565
598
|
}
|
|
566
599
|
}
|
|
567
600
|
|
|
568
|
-
async
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
601
|
+
async getVesuAPYs() {
|
|
602
|
+
const vesuAdapters = this.getVesuAdapters();
|
|
603
|
+
const allVesuPools = await VesuAdapter.getVesuPools();
|
|
604
|
+
const pools = vesuAdapters.map((vesuAdapter) => {
|
|
605
|
+
return allVesuPools.pools.find(p => vesuAdapter.config.poolId.eqString(num.getHexString(p.id)));
|
|
606
|
+
});
|
|
607
|
+
logger.verbose(`${this.metadata.name}::netAPY: vesu-pools: ${JSON.stringify(pools)}`);
|
|
608
|
+
if (pools.some(p => !p)) {
|
|
609
|
+
throw new Error('Pool not found');
|
|
575
610
|
}
|
|
576
|
-
|
|
611
|
+
const positions = await this.getVesuPositions();
|
|
612
|
+
logger.verbose(`${this.metadata.name}::netAPY: positions: ${JSON.stringify(positions)}`);
|
|
613
|
+
const baseAPYs: number[] = [];
|
|
614
|
+
const rewardAPYs: number[] = [];
|
|
615
|
+
|
|
616
|
+
for (const [index, pool] of pools.entries()) {
|
|
617
|
+
const vesuAdapter = vesuAdapters[index];
|
|
618
|
+
const collateralAsset = pool.assets.find((a: any) => a.symbol.toLowerCase() === vesuAdapter.config.collateral.symbol.toLowerCase())?.stats!;
|
|
619
|
+
const debtAsset = pool.assets.find((a: any) => a.symbol.toLowerCase() === vesuAdapter.config.debt.symbol.toLowerCase())?.stats!;
|
|
620
|
+
const supplyApy = Number(collateralAsset.supplyApy.value || 0) / 1e18;
|
|
621
|
+
|
|
622
|
+
const lstAPY = await this.getLSTAPR(vesuAdapter.config.collateral.address);
|
|
623
|
+
logger.verbose(`${this.metadata.name}::netAPY: ${vesuAdapter.config.collateral.symbol} LST APR from Endur: ${lstAPY}`);
|
|
624
|
+
|
|
625
|
+
baseAPYs.push(...[supplyApy + lstAPY, Number(debtAsset.borrowApr.value) / 1e18]);
|
|
626
|
+
rewardAPYs.push(...[Number(collateralAsset.defiSpringSupplyApr?.value || "0") / 1e18, 0]);
|
|
627
|
+
}
|
|
628
|
+
logger.verbose(`${this.metadata.name}::netAPY: baseAPYs: ${JSON.stringify(baseAPYs)}`);
|
|
629
|
+
logger.verbose(`${this.metadata.name}::netAPY: rewardAPYs: ${JSON.stringify(rewardAPYs)}`);
|
|
630
|
+
|
|
631
|
+
assert(baseAPYs.length == positions.length, 'APYs and positions length mismatch');
|
|
632
|
+
|
|
633
|
+
return {
|
|
634
|
+
baseAPYs,
|
|
635
|
+
rewardAPYs,
|
|
636
|
+
positions
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
protected async returnNetAPY(baseAPYs: number[], rewardAPYs: number[], weights: number[], prevAUMUSD: Web3Number) {
|
|
641
|
+
if (weights.every(p => p == 0)) {
|
|
642
|
+
return {
|
|
643
|
+
net: 0, splits: [{
|
|
644
|
+
apy: 0, id: 'base'
|
|
645
|
+
}, {
|
|
646
|
+
apy: 0, id: 'defispring'
|
|
647
|
+
}]
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const baseAPY = this.computeAPY(baseAPYs, weights, prevAUMUSD);
|
|
652
|
+
const rewardAPY = this.computeAPY(rewardAPYs, weights, prevAUMUSD);
|
|
653
|
+
const netAPY = baseAPY + rewardAPY;
|
|
654
|
+
logger.verbose(`${this.metadata.name}::netAPY: net: ${netAPY}, baseAPY: ${baseAPY}, rewardAPY: ${rewardAPY}`);
|
|
655
|
+
return {
|
|
656
|
+
net: netAPY, splits: [{
|
|
657
|
+
apy: baseAPY, id: 'base'
|
|
658
|
+
}, {
|
|
659
|
+
apy: rewardAPY, id: 'defispring'
|
|
660
|
+
}]
|
|
661
|
+
};
|
|
577
662
|
}
|
|
578
663
|
|
|
579
664
|
protected async getUnusedBalanceAPY() {
|
|
580
665
|
const unusedBalance = await this.getUnusedBalance();
|
|
581
|
-
const
|
|
666
|
+
const vesuAdapter = this.getVesuSameTokenAdapter();
|
|
667
|
+
const underlying = vesuAdapter.config.debt;
|
|
582
668
|
const lstAPY = await this.getLSTAPR(underlying.address);
|
|
583
669
|
return {
|
|
584
670
|
apy: lstAPY,
|
|
585
|
-
weight: unusedBalance.usdValue
|
|
671
|
+
weight: unusedBalance.usdValue
|
|
586
672
|
};
|
|
587
673
|
}
|
|
588
674
|
|
|
675
|
+
private computeAPY(apys: number[], weights: number[], currentAUM: Web3Number) {
|
|
676
|
+
assert(apys.length === weights.length, "APYs and weights length mismatch");
|
|
677
|
+
const weightedSum = apys.reduce((acc, apy, i) => acc + apy * weights[i], 0);
|
|
678
|
+
logger.verbose(`${this.getTag()} computeAPY: apys: ${JSON.stringify(apys)}, weights: ${JSON.stringify(weights)}, weightedSum: ${weightedSum}, currentAUM: ${currentAUM}`);
|
|
679
|
+
return weightedSum / currentAUM.toNumber();
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
getVesuAdapters(): VesuAdapter[] {
|
|
683
|
+
const multiply = this.getVesuMultiplyAdapters();
|
|
684
|
+
return multiply.map(m => m._vesuAdapter);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
async getVesuPositions(blockNumber: BlockIdentifier = 'latest'): Promise<VaultPosition[]> {
|
|
688
|
+
const adapters = this.getVesuAdapters();
|
|
689
|
+
const positions: VaultPosition[] = [];
|
|
690
|
+
for (const adapter of adapters) {
|
|
691
|
+
positions.push(...await adapter.getPositions(this.config, blockNumber));
|
|
692
|
+
}
|
|
693
|
+
return positions;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async getUnusedBalance(): Promise<SingleTokenInfo> {
|
|
697
|
+
const balance = await (new ERC20(this.config)).balanceOf(this.asset().address, this.metadata.additionalInfo.vaultAllocator, this.asset().decimals);
|
|
698
|
+
const price = await this.pricer.getPrice(this.metadata.depositTokens[0].symbol);
|
|
699
|
+
const usdValue = Number(balance.toFixed(6)) * price.price;
|
|
700
|
+
return {
|
|
701
|
+
tokenInfo: this.asset(),
|
|
702
|
+
amount: balance,
|
|
703
|
+
usdValue
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
async maxNewDeposits(
|
|
708
|
+
params: { isAPYComputation: boolean } = { isAPYComputation: false },
|
|
709
|
+
) {
|
|
710
|
+
const maxBorrowableAmounts = await this.getMaxBorrowableAmount(params);
|
|
711
|
+
let numerator = 0;
|
|
712
|
+
|
|
713
|
+
for (let adapter of this.getVesuAdapters()) {
|
|
714
|
+
const maxBorrowableAmountInfo = maxBorrowableAmounts.maxBorrowables.find(
|
|
715
|
+
(b) => b.borrowableAsset.address.eq(adapter.config.debt.address),
|
|
716
|
+
);
|
|
717
|
+
if (!maxBorrowableAmountInfo || !maxBorrowableAmountInfo?.amount) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
`Max borrowable amount not found for adapter: ${adapter.config.debt.symbol}`,
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
numerator +=
|
|
724
|
+
(this.metadata.additionalInfo.targetHealthFactor *
|
|
725
|
+
maxBorrowableAmountInfo.amount.toNumber()) /
|
|
726
|
+
maxBorrowableAmountInfo.ltv;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return numerator - maxBorrowableAmounts.netMaxBorrowableAmount.toNumber();
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
async getMaxBorrowableAmount(
|
|
733
|
+
params: { isAPYComputation: boolean } = { isAPYComputation: false },
|
|
734
|
+
) {
|
|
735
|
+
const vesuAdapters = this.getVesuAdapters();
|
|
736
|
+
let netMaxBorrowableAmount = Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals);
|
|
737
|
+
const maxBorrowables: {amount: Web3Number, dexSwappableAmount: Web3Number, maxBorrowableAmount: Web3Number, borrowableAsset: TokenInfo, ltv: number, poolId: ContractAddr}[] = [];
|
|
738
|
+
for (const vesuAdapter of vesuAdapters) {
|
|
739
|
+
const output = await this.getMaxBorrowableAmountByVesuAdapter(
|
|
740
|
+
vesuAdapter,
|
|
741
|
+
params.isAPYComputation,
|
|
742
|
+
);
|
|
743
|
+
const ltv = await vesuAdapter.getLTVConfig(this.config);
|
|
744
|
+
maxBorrowables.push({...output, ltv, poolId: vesuAdapter.config.poolId});
|
|
745
|
+
}
|
|
746
|
+
maxBorrowables.sort((a, b) => b.amount.toNumber() - a.amount.toNumber());
|
|
747
|
+
netMaxBorrowableAmount = maxBorrowables.reduce(
|
|
748
|
+
(acc, curr) => acc.plus(curr.amount),
|
|
749
|
+
Web3Number.fromWei("0", this.getLSTUnderlyingTokenInfo().decimals),
|
|
750
|
+
);
|
|
751
|
+
return { netMaxBorrowableAmount, maxBorrowables };
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
async getMaxSwappableWithMaxSlippage(
|
|
755
|
+
fromToken: TokenInfo,
|
|
756
|
+
toToken: TokenInfo,
|
|
757
|
+
maxSlippage: number,
|
|
758
|
+
maxAmount: Web3Number,
|
|
759
|
+
) {
|
|
760
|
+
const output = await findMaxInputWithSlippage({
|
|
761
|
+
apiGetOutput: async (inputAmount: number): Promise<number> => {
|
|
762
|
+
const ekuboQuoter = new EkuboQuoter(this.config, this.pricer);
|
|
763
|
+
await new Promise((resolve) => setTimeout(resolve, 1000)); // artificial delay, to avoid rate limit
|
|
764
|
+
const quote = await ekuboQuoter.getQuoteExactInput(
|
|
765
|
+
fromToken.address.address,
|
|
766
|
+
toToken.address.address,
|
|
767
|
+
new Web3Number(inputAmount.toFixed(9), fromToken.decimals),
|
|
768
|
+
);
|
|
769
|
+
return Web3Number.fromWei(
|
|
770
|
+
quote.total_calculated.toString(),
|
|
771
|
+
toToken.decimals,
|
|
772
|
+
).toNumber();
|
|
773
|
+
},
|
|
774
|
+
maxInput: maxAmount.toNumber(),
|
|
775
|
+
maxSlippagePercent: maxSlippage,
|
|
776
|
+
tolerance: 0.001,
|
|
777
|
+
referenceRate: 1,
|
|
778
|
+
});
|
|
779
|
+
return new Web3Number(output.optimalInput, fromToken.decimals);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
async getMaxBorrowableAmountByVesuAdapter(
|
|
783
|
+
vesuAdapter: VesuAdapter,
|
|
784
|
+
isAPYComputation: boolean,
|
|
785
|
+
) {
|
|
786
|
+
const lstAPY = await this.getLSTAPR(
|
|
787
|
+
this.getLSTUnderlyingTokenInfo().address,
|
|
788
|
+
);
|
|
789
|
+
const maxInterestRate = lstAPY * 0.8;
|
|
790
|
+
const { maxDebtToHave: maxBorrowableAmount, currentDebt } =
|
|
791
|
+
await vesuAdapter.getMaxBorrowableByInterestRate(
|
|
792
|
+
this.config,
|
|
793
|
+
vesuAdapter.config.debt,
|
|
794
|
+
maxInterestRate,
|
|
795
|
+
);
|
|
796
|
+
const debtCap = await vesuAdapter.getDebtCap(this.config);
|
|
797
|
+
|
|
798
|
+
if (currentDebt.gte(debtCap)) {
|
|
799
|
+
return {
|
|
800
|
+
amount: Web3Number.fromWei("0", vesuAdapter.config.debt.decimals),
|
|
801
|
+
dexSwappableAmount: Web3Number.fromWei(
|
|
802
|
+
"0",
|
|
803
|
+
vesuAdapter.config.debt.decimals,
|
|
804
|
+
),
|
|
805
|
+
maxBorrowableAmount: Web3Number.fromWei(
|
|
806
|
+
"0",
|
|
807
|
+
vesuAdapter.config.debt.decimals,
|
|
808
|
+
),
|
|
809
|
+
borrowableAsset: vesuAdapter.config.debt,
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
const availableToBorrow = debtCap.minus(currentDebt);
|
|
814
|
+
|
|
815
|
+
const maxBorrowable = maxBorrowableAmount
|
|
816
|
+
.minimum(availableToBorrow)
|
|
817
|
+
.multipliedBy(0.999);
|
|
818
|
+
// Dont compute precise max swappable for APY computation
|
|
819
|
+
if (
|
|
820
|
+
vesuAdapter.config.debt.address.eq(
|
|
821
|
+
this.getLSTUnderlyingTokenInfo().address,
|
|
822
|
+
) ||
|
|
823
|
+
isAPYComputation
|
|
824
|
+
) {
|
|
825
|
+
return {
|
|
826
|
+
amount: maxBorrowable,
|
|
827
|
+
dexSwappableAmount: maxBorrowable,
|
|
828
|
+
maxBorrowableAmount: maxBorrowable,
|
|
829
|
+
borrowableAsset: vesuAdapter.config.debt,
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
// Want < 0.02% slippage
|
|
833
|
+
try {
|
|
834
|
+
const maxSwappable = await this.getMaxSwappableWithMaxSlippage(
|
|
835
|
+
vesuAdapter.config.debt,
|
|
836
|
+
this.getLSTUnderlyingTokenInfo(),
|
|
837
|
+
0.0002,
|
|
838
|
+
maxBorrowable,
|
|
839
|
+
);
|
|
840
|
+
return {
|
|
841
|
+
amount: maxBorrowable.minimum(maxSwappable),
|
|
842
|
+
dexSwappableAmount: maxSwappable,
|
|
843
|
+
maxBorrowableAmount: maxBorrowable,
|
|
844
|
+
borrowableAsset: vesuAdapter.config.debt,
|
|
845
|
+
};
|
|
846
|
+
} catch (error) {
|
|
847
|
+
logger.warn(`${this.getTag()}: Failed to get max swappable: ${error}`);
|
|
848
|
+
const maxSwappable = Web3Number.fromWei(
|
|
849
|
+
"0",
|
|
850
|
+
vesuAdapter.config.debt.decimals,
|
|
851
|
+
);
|
|
852
|
+
return {
|
|
853
|
+
amount: maxBorrowable.minimum(maxSwappable),
|
|
854
|
+
dexSwappableAmount: maxSwappable,
|
|
855
|
+
maxBorrowableAmount: maxBorrowable,
|
|
856
|
+
borrowableAsset: vesuAdapter.config.debt,
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
589
861
|
async getAUM(unrealizedAUM: boolean = false): Promise<{ net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[] }> {
|
|
590
862
|
const underlying = this.asset();
|
|
591
863
|
assert(underlying.symbol.startsWith('x'), 'Underlying is not an LST of Endur');
|
|
@@ -1002,6 +1274,9 @@ const hyperxWBTC: HyperLSTStrategySettings = {
|
|
|
1002
1274
|
borrowable_assets: [{
|
|
1003
1275
|
tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
|
|
1004
1276
|
poolId: VesuPools.Re7xBTC,
|
|
1277
|
+
}, {
|
|
1278
|
+
tokenInfo: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
|
|
1279
|
+
poolId: VesuPools.Prime,
|
|
1005
1280
|
}],
|
|
1006
1281
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
|
|
1007
1282
|
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!.decimals),
|
|
@@ -1119,7 +1394,7 @@ function createHyperLSTSettings(
|
|
|
1119
1394
|
)!;
|
|
1120
1395
|
return {
|
|
1121
1396
|
isPaused: false,
|
|
1122
|
-
liveStatus: StrategyLiveStatus.HOT,
|
|
1397
|
+
liveStatus: lstSymbol === 'xSTRK' ? StrategyLiveStatus.HOT : ((lstSymbol == 'xWBTC' || lstSymbol == 'xtBTC') ? StrategyLiveStatus.ACTIVE : StrategyLiveStatus.DEPRECATED),
|
|
1123
1398
|
isAudited: true,
|
|
1124
1399
|
isInstantWithdrawal: false,
|
|
1125
1400
|
hideHarvestInfo: true,
|
|
@@ -1158,8 +1433,7 @@ const HYPER_LST_SECURITY = {
|
|
|
1158
1433
|
},
|
|
1159
1434
|
accessControl: {
|
|
1160
1435
|
type: AccessControlType.STANDARD_ACCOUNT,
|
|
1161
|
-
addresses: [ContractAddr.from("
|
|
1162
|
-
timeLock: "2 Days",
|
|
1436
|
+
addresses: [ContractAddr.from("0x03495dd1e4838aa06666aac236036d86e81a6553e222fc02e70c2cbc0062e8d0")],
|
|
1163
1437
|
},
|
|
1164
1438
|
};
|
|
1165
1439
|
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
RiskFactor,
|
|
10
10
|
SourceCodeType,
|
|
11
11
|
StrategyLiveStatus,
|
|
12
|
+
TokenIndexingType,
|
|
12
13
|
TokenInfo,
|
|
13
14
|
UnwrapLabsCurator,
|
|
14
15
|
Protocols,
|
|
@@ -878,6 +879,7 @@ const vesuPrimeUSDC: TokenInfo = {
|
|
|
878
879
|
decimals: 18,
|
|
879
880
|
logo: usdc.logo,
|
|
880
881
|
displayDecimals: 2,
|
|
882
|
+
indexingType: TokenIndexingType.IGNORE,
|
|
881
883
|
};
|
|
882
884
|
|
|
883
885
|
const strk = Global.getDefaultTokens().find((t) => t.symbol === "STRK")!;
|