@strkfarm/sdk 2.0.0-dev.27 → 2.0.0-dev.28
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 +79130 -49357
- package/dist/index.browser.mjs +18039 -11434
- package/dist/index.d.ts +2869 -898
- package/dist/index.js +19036 -12210
- package/dist/index.mjs +18942 -12161
- package/package.json +1 -1
- package/src/data/avnu.abi.json +840 -0
- package/src/data/ekubo-price-fethcer.abi.json +265 -0
- package/src/dataTypes/_bignumber.ts +13 -4
- package/src/dataTypes/index.ts +3 -2
- package/src/dataTypes/mynumber.ts +141 -0
- package/src/global.ts +76 -41
- package/src/index.browser.ts +2 -1
- package/src/interfaces/common.tsx +167 -2
- package/src/modules/ExtendedWrapperSDk/types.ts +26 -4
- package/src/modules/ExtendedWrapperSDk/wrapper.ts +110 -67
- package/src/modules/apollo-client-config.ts +28 -0
- package/src/modules/avnu.ts +4 -4
- package/src/modules/ekubo-pricer.ts +79 -0
- package/src/modules/ekubo-quoter.ts +46 -30
- package/src/modules/erc20.ts +17 -0
- package/src/modules/harvests.ts +43 -29
- package/src/modules/pragma.ts +23 -8
- package/src/modules/pricer-from-api.ts +156 -15
- package/src/modules/pricer-lst.ts +1 -1
- package/src/modules/pricer.ts +40 -4
- package/src/modules/pricerBase.ts +2 -1
- package/src/node/deployer.ts +36 -1
- package/src/node/pricer-redis.ts +2 -1
- package/src/strategies/base-strategy.ts +78 -10
- package/src/strategies/ekubo-cl-vault.tsx +906 -347
- package/src/strategies/factory.ts +159 -0
- package/src/strategies/index.ts +6 -1
- package/src/strategies/registry.ts +239 -0
- package/src/strategies/sensei.ts +335 -7
- package/src/strategies/svk-strategy.ts +97 -27
- package/src/strategies/types.ts +4 -0
- package/src/strategies/universal-adapters/adapter-utils.ts +2 -1
- package/src/strategies/universal-adapters/avnu-adapter.ts +177 -268
- package/src/strategies/universal-adapters/baseAdapter.ts +263 -251
- package/src/strategies/universal-adapters/common-adapter.ts +206 -203
- package/src/strategies/universal-adapters/extended-adapter.ts +155 -336
- package/src/strategies/universal-adapters/index.ts +9 -8
- package/src/strategies/universal-adapters/token-transfer-adapter.ts +200 -0
- package/src/strategies/universal-adapters/usdc<>usdce-adapter.ts +200 -0
- package/src/strategies/universal-adapters/vesu-adapter.ts +110 -75
- package/src/strategies/universal-adapters/vesu-modify-position-adapter.ts +476 -0
- package/src/strategies/universal-adapters/vesu-multiply-adapter.ts +762 -844
- package/src/strategies/universal-adapters/vesu-position-common.ts +251 -0
- package/src/strategies/universal-adapters/vesu-supply-only-adapter.ts +18 -3
- package/src/strategies/universal-lst-muliplier-strategy.tsx +396 -204
- package/src/strategies/universal-strategy.tsx +1426 -1178
- package/src/strategies/vesu-extended-strategy/services/executionService.ts +2251 -0
- package/src/strategies/vesu-extended-strategy/services/extended-vesu-state-manager.ts +2941 -0
- package/src/strategies/vesu-extended-strategy/services/operationService.ts +12 -1
- package/src/strategies/vesu-extended-strategy/types/transaction-metadata.ts +52 -0
- package/src/strategies/vesu-extended-strategy/utils/config.runtime.ts +1 -0
- package/src/strategies/vesu-extended-strategy/utils/constants.ts +2 -0
- package/src/strategies/vesu-extended-strategy/utils/helper.ts +158 -124
- package/src/strategies/vesu-extended-strategy/vesu-extended-strategy.tsx +377 -1788
- package/src/strategies/vesu-rebalance.tsx +255 -152
- package/src/utils/health-factor-math.ts +4 -1
- package/src/utils/index.ts +2 -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/strategies/universal-adapters/unused-balance-adapter.ts +0 -109
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import { FAQ, getMainnetConfig, getNoRiskTags, highlightTextWithLinks, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, TokenInfo, VaultPosition } from "@/interfaces";
|
|
1
|
+
import { FAQ, getMainnetConfig, getNoRiskTags, highlightTextWithLinks, IConfig, IStrategyMetadata, Protocols, RiskFactor, RiskType, TokenInfo, VaultPosition, StrategyTag, AuditStatus, SourceCodeType, AccessControlType, InstantWithdrawalVault, StrategyLiveStatus, StrategySettings, VaultType, RedemptionInfo } from "@/interfaces";
|
|
2
2
|
import { AUMTypes, getContractDetails, UNIVERSAL_MANAGE_IDS, UniversalManageCall, UniversalStrategySettings } from "./universal-strategy";
|
|
3
3
|
import { PricerBase } from "@/modules/pricerBase";
|
|
4
4
|
import { ContractAddr, Web3Number } from "@/dataTypes";
|
|
5
5
|
import { Global } from "@/global";
|
|
6
|
-
import { ApproveCallParams, APYType, AvnuSwapCallParams, BaseAdapterConfig, CommonAdapter, ManageCall, PositionInfo,
|
|
6
|
+
import { ApproveCallParams, APYType, AvnuSwapCallParams, BaseAdapterConfig, CommonAdapter, ManageCall, PositionInfo, TokenTransferAdapter, VesuMultiplyAdapter, VesuPools, VesuSupplyOnlyAdapter } from "./universal-adapters";
|
|
7
7
|
import { AVNU_EXCHANGE } from "./universal-adapters/adapter-utils";
|
|
8
8
|
import { DepegRiskLevel, LiquidationRiskLevel, SmartContractRiskLevel, TechnicalRiskLevel } from "@/interfaces/risks";
|
|
9
|
-
import { AvnuWrapper, EkuboQuoter, ERC20, PricerFromApi, PricerLST } from "@/modules";
|
|
9
|
+
import { AvnuWrapper, EkuboQuoter, ERC20, LSTAPRService, PricerFromApi, PricerLST } from "@/modules";
|
|
10
10
|
import { assert, logger } from "@/utils";
|
|
11
11
|
import { SingleActionAmount, SingleTokenInfo } from "./base-strategy";
|
|
12
12
|
import { SVKStrategy } from "./svk-strategy";
|
|
13
|
-
import { Call, uint256 } from "starknet";
|
|
13
|
+
import { Call, Contract, uint256 } from "starknet";
|
|
14
14
|
import ERC4626Abi from "@/data/erc4626.abi.json";
|
|
15
15
|
import { AdapterOptimizer } from "./universal-adapters/adapter-optimizer";
|
|
16
|
+
import { LSTPriceType } from "./types";
|
|
16
17
|
import { MINIMUM_WBTC_DIFFERENCE_FOR_AVNU_SWAP } from "./vesu-extended-strategy/utils/constants";
|
|
17
18
|
import { VesuExtendedStrategySettings } from "./vesu-extended-strategy/vesu-extended-strategy";
|
|
18
19
|
|
|
@@ -28,7 +29,7 @@ export interface HyperLSTStrategySettings extends UniversalStrategySettings {
|
|
|
28
29
|
export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings> extends SVKStrategy<S> {
|
|
29
30
|
|
|
30
31
|
private quoteAmountToFetchPrice = new Web3Number(1, 18);
|
|
31
|
-
|
|
32
|
+
|
|
32
33
|
constructor(config: IConfig, pricer: PricerBase, metadata: IStrategyMetadata<S>) {
|
|
33
34
|
super(config, pricer, metadata);
|
|
34
35
|
const STRKToken = Global.getDefaultTokens().find(token => token.symbol === 'STRK')!;
|
|
@@ -43,9 +44,9 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
43
44
|
this.metadata.additionalInfo.adapters.forEach(adapter => {
|
|
44
45
|
adapter.adapter.config.networkConfig = this.config;
|
|
45
46
|
adapter.adapter.config.pricer = this.pricer;
|
|
46
|
-
if ((adapter.adapter as
|
|
47
|
-
(adapter.adapter as
|
|
48
|
-
(adapter.adapter as
|
|
47
|
+
if ((adapter.adapter as any)._vesuAdapter) {
|
|
48
|
+
(adapter.adapter as any)._vesuAdapter.networkConfig = this.config;
|
|
49
|
+
(adapter.adapter as any)._vesuAdapter.pricer = this.pricer;
|
|
49
50
|
}
|
|
50
51
|
});
|
|
51
52
|
}
|
|
@@ -83,6 +84,102 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
83
84
|
return vesuMultipleAdapters;
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
async getRewardsAUM(_prevAum: Web3Number) {
|
|
88
|
+
const lstToken = this.asset();
|
|
89
|
+
// Defi spring rewards handling; currently returns 0 as SVKStrategy has no built-in rewards logic
|
|
90
|
+
return Web3Number.fromWei("0", lstToken.decimals);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getLSTDexPrice() {
|
|
94
|
+
const ekuboQuoter = new EkuboQuoter(this.config, this.pricer);
|
|
95
|
+
const lstTokenInfo = this.asset();
|
|
96
|
+
const lstUnderlyingTokenInfo = this.getLSTUnderlyingTokenInfo();
|
|
97
|
+
const quote = await ekuboQuoter.getQuoteExactInput(
|
|
98
|
+
lstTokenInfo.address.address,
|
|
99
|
+
lstUnderlyingTokenInfo.address.address,
|
|
100
|
+
this.quoteAmountToFetchPrice,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const outputAmount = Web3Number.fromWei(
|
|
104
|
+
quote.total_calculated,
|
|
105
|
+
lstUnderlyingTokenInfo.decimals,
|
|
106
|
+
);
|
|
107
|
+
const price =
|
|
108
|
+
outputAmount.toNumber() / this.quoteAmountToFetchPrice.toNumber();
|
|
109
|
+
logger.verbose(`${this.getTag()}:: LST Dex Price: ${price}`);
|
|
110
|
+
return price;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async getLSTAvnuRate() {
|
|
114
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter();
|
|
115
|
+
const lstTokenInfo = vesuAdapter1._vesuAdapter.config.collateral;
|
|
116
|
+
const underlyingTokenInfo = vesuAdapter1._vesuAdapter.config.debt;
|
|
117
|
+
|
|
118
|
+
const avnuModule = new AvnuWrapper();
|
|
119
|
+
|
|
120
|
+
const sellAmount = (lstTokenInfo as any).priceCheckAmount
|
|
121
|
+
? new Web3Number(
|
|
122
|
+
(lstTokenInfo as any).priceCheckAmount,
|
|
123
|
+
underlyingTokenInfo.decimals,
|
|
124
|
+
)
|
|
125
|
+
: new Web3Number(1, underlyingTokenInfo.decimals);
|
|
126
|
+
|
|
127
|
+
const quote = await avnuModule.getQuotes(
|
|
128
|
+
underlyingTokenInfo.address.address,
|
|
129
|
+
lstTokenInfo.address.address,
|
|
130
|
+
sellAmount.toWei(),
|
|
131
|
+
this.metadata.additionalInfo.vaultAllocator.address,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const underlyingAmountNumber = sellAmount.toNumber();
|
|
135
|
+
const lstAmountNumber = Web3Number.fromWei(
|
|
136
|
+
quote.buyAmount.toString(),
|
|
137
|
+
lstTokenInfo.decimals,
|
|
138
|
+
).toNumber();
|
|
139
|
+
|
|
140
|
+
assert(lstAmountNumber > 0, "Avnu LST amount is zero");
|
|
141
|
+
|
|
142
|
+
const exchangeRate = underlyingAmountNumber / lstAmountNumber;
|
|
143
|
+
logger.verbose(
|
|
144
|
+
`${this.getTag()}:: LST Avnu Exchange Rate: ${exchangeRate}`,
|
|
145
|
+
);
|
|
146
|
+
return exchangeRate;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async getLSTExchangeRate() {
|
|
150
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter();
|
|
151
|
+
const lstTokenInfo = vesuAdapter1._vesuAdapter.config.collateral;
|
|
152
|
+
const lstABI = new Contract({
|
|
153
|
+
abi: ERC4626Abi,
|
|
154
|
+
address: lstTokenInfo.address.address,
|
|
155
|
+
providerOrAccount: this.config.provider,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const price: any = await lstABI.call("convert_to_assets", [
|
|
159
|
+
uint256.bnToUint256(new Web3Number(1, lstTokenInfo.decimals).toWei()),
|
|
160
|
+
]);
|
|
161
|
+
const exchangeRate =
|
|
162
|
+
Number(uint256.uint256ToBN(price).toString()) /
|
|
163
|
+
Math.pow(10, lstTokenInfo.decimals);
|
|
164
|
+
logger.verbose(`${this.getTag()}:: LST Exchange Rate: ${exchangeRate}`);
|
|
165
|
+
return exchangeRate;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
|
|
169
|
+
const lstTruePrice = await this.getLSTExchangeRate();
|
|
170
|
+
const minOutputAmount = amountInUnderlying
|
|
171
|
+
.dividedBy(lstTruePrice)
|
|
172
|
+
.multipliedBy(0.99979);
|
|
173
|
+
return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
|
|
177
|
+
const lstTruePrice = await this.getLSTExchangeRate();
|
|
178
|
+
const minOutputAmount = amountInLST
|
|
179
|
+
.multipliedBy(lstTruePrice)
|
|
180
|
+
.multipliedBy(0.995);
|
|
181
|
+
return minOutputAmount;
|
|
182
|
+
}
|
|
86
183
|
|
|
87
184
|
// async getAvnuSwapMultiplyCall(params: {
|
|
88
185
|
// isDeposit: boolean,
|
|
@@ -132,7 +229,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
132
229
|
|
|
133
230
|
// async _getAvnuDepositSwapLegCall(params: {
|
|
134
231
|
// isDeposit: boolean,
|
|
135
|
-
// leg1DepositAmount: Web3Number,
|
|
232
|
+
// leg1DepositAmount: Web3Number,
|
|
136
233
|
// minHF: number, // e.g. 1.01
|
|
137
234
|
// vesuAdapter: VesuAdapter
|
|
138
235
|
// }) {
|
|
@@ -144,18 +241,18 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
144
241
|
// // approve and swap strk
|
|
145
242
|
// // add collateral again
|
|
146
243
|
|
|
147
|
-
|
|
244
|
+
|
|
148
245
|
// const legLTV = await vesuAdapter.getLTVConfig(this.config);
|
|
149
246
|
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall legLTV: ${legLTV}`);
|
|
150
247
|
// const existingPositions = await vesuAdapter.getPositions(this.config);
|
|
151
248
|
// const collateralisation = await vesuAdapter.getCollateralization(this.config);
|
|
152
249
|
// const existingCollateralInfo = existingPositions[0];
|
|
153
250
|
// const existingDebtInfo = existingPositions[1];
|
|
154
|
-
// logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
251
|
+
// logger.debug(`${this.getTag()}::_getAvnuDepositSwapLegCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
155
252
|
// existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
156
253
|
|
|
157
254
|
// // - Prices as seen by Vesu contracts, ideal for HF math
|
|
158
|
-
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
255
|
+
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
159
256
|
// // collateral and debt as equal.
|
|
160
257
|
// const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
|
|
161
258
|
// const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
|
|
@@ -202,7 +299,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
202
299
|
|
|
203
300
|
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall debtAmount: ${debtAmount}`);
|
|
204
301
|
// if (debtAmount.lt(0)) {
|
|
205
|
-
// // this is to unwind the position to optimal HF.
|
|
302
|
+
// // this is to unwind the position to optimal HF.
|
|
206
303
|
// const lstDEXPrice = await this.getLSTDexPrice();
|
|
207
304
|
// const debtAmountInLST = debtAmount.abs().dividedBy(lstDEXPrice);
|
|
208
305
|
// const calls = await this.getVesuMultiplyCall({
|
|
@@ -228,7 +325,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
228
325
|
// }));
|
|
229
326
|
|
|
230
327
|
// console.log(`manageCall1`, manageCall1.call, debtAmount.toWei(), newDepositAmount.toWei());
|
|
231
|
-
|
|
328
|
+
|
|
232
329
|
// const proofIds: string[] = [STEP0, STEP1];
|
|
233
330
|
// const manageCalls: ManageCall[] = [manageCall0, manageCall1];
|
|
234
331
|
|
|
@@ -253,9 +350,9 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
253
350
|
// const minAmountWei = (minAmount).toWei();
|
|
254
351
|
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall minAmount: ${minAmount}`);
|
|
255
352
|
// const swapInfo = await avnuModule.getSwapInfo(
|
|
256
|
-
// quote,
|
|
257
|
-
// this.metadata.additionalInfo.vaultAllocator.address,
|
|
258
|
-
// 0,
|
|
353
|
+
// quote,
|
|
354
|
+
// this.metadata.additionalInfo.vaultAllocator.address,
|
|
355
|
+
// 0,
|
|
259
356
|
// this.address.address,
|
|
260
357
|
// minAmountWei
|
|
261
358
|
// );
|
|
@@ -271,7 +368,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
271
368
|
|
|
272
369
|
|
|
273
370
|
// // if the created debt, when added is collateral will put the total HF above min, but below (target + 0.05),
|
|
274
|
-
// // then lets close the looping cycle by adding this as collateral.
|
|
371
|
+
// // then lets close the looping cycle by adding this as collateral.
|
|
275
372
|
// const newCollateral = minAmount.plus(totalCollateral);
|
|
276
373
|
// const newHF = newCollateral.multipliedBy(collateralPrice).multipliedBy(legLTV).dividedBy(totalDebtAmount).dividedBy(debtPrice).toNumber();
|
|
277
374
|
// logger.verbose(`${this.getTag()}::_getAvnuDepositSwapLegCall newHF: ${newHF}`);
|
|
@@ -283,7 +380,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
283
380
|
// const manageCall4 = manage4Info.callConstructor({
|
|
284
381
|
// amount: minAmount
|
|
285
382
|
// });
|
|
286
|
-
|
|
383
|
+
|
|
287
384
|
// const STEP5 = getVesuLegId(UNIVERSAL_MANAGE_IDS.VESU_LEG1, vesuAdapter.config.debt.symbol);
|
|
288
385
|
// const manage5Info = this.getProofs<VesuModifyPositionCallParams>(STEP5);
|
|
289
386
|
// const manageCall5 = manage5Info.callConstructor(VesuAdapter.getDefaultModifyPositionCallParams({
|
|
@@ -301,7 +398,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
301
398
|
// return manageCall;
|
|
302
399
|
// }
|
|
303
400
|
|
|
304
|
-
// todo unwind or not deposit when the yield is bad.
|
|
401
|
+
// todo unwind or not deposit when the yield is bad.
|
|
305
402
|
|
|
306
403
|
// async getLSTMultiplierRebalanceCall(): Promise<{ shouldRebalance: boolean, manageCalls: {vesuAdapter: VesuAdapter, manageCall: Call}[] }> {
|
|
307
404
|
// let shouldRebalance = false;
|
|
@@ -327,11 +424,11 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
327
424
|
// const healthFactor = await vesuAdapter.getHealthFactor();
|
|
328
425
|
|
|
329
426
|
// const collateralisation = await vesuAdapter.getCollateralization(this.config);
|
|
330
|
-
// logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
427
|
+
// logger.debug(`${this.getTag()}::getVesuMultiplyCall::${vesuAdapter.config.debt.symbol} existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
331
428
|
// existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
332
429
|
|
|
333
430
|
// // - Prices as seen by Vesu contracts, ideal for HF math
|
|
334
|
-
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
431
|
+
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
335
432
|
// // collateral and debt as equal.
|
|
336
433
|
// const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
|
|
337
434
|
// const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
|
|
@@ -341,11 +438,11 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
341
438
|
// const isHFTooLow = healthFactor < this.metadata.additionalInfo.minHealthFactor;
|
|
342
439
|
// const isHFTooHigh = healthFactor > this.metadata.additionalInfo.targetHealthFactor + 0.05;
|
|
343
440
|
// if (isHFTooLow || isHFTooHigh || 1) {
|
|
344
|
-
// // use unused collateral to target more.
|
|
441
|
+
// // use unused collateral to target more.
|
|
345
442
|
// const manageCall = await this._getAvnuDepositSwapLegCall({
|
|
346
443
|
// isDeposit: true,
|
|
347
444
|
// leg1DepositAmount: unusedBalance.amount,
|
|
348
|
-
// minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
|
|
445
|
+
// minHF: 1.02, // todo, shouldnt use this 1.02 HF, if there isn;t more looping left.
|
|
349
446
|
// vesuAdapter
|
|
350
447
|
// })
|
|
351
448
|
// return { shouldRebalance: true, manageCall };
|
|
@@ -355,61 +452,17 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
355
452
|
// }
|
|
356
453
|
// }
|
|
357
454
|
|
|
358
|
-
// protected async getVesuAUM(adapter: VesuAdapter) {
|
|
359
|
-
// const legAUM = await adapter.getPositions(this.config);
|
|
360
|
-
// const underlying = this.asset();
|
|
361
|
-
// // assert its an LST of Endur
|
|
362
|
-
// assert(underlying.symbol.startsWith('x'), 'Underlying is not an LST of Endur');
|
|
363
|
-
|
|
364
|
-
// let vesuAum = Web3Number.fromWei("0", underlying.decimals);
|
|
365
|
-
|
|
366
|
-
// let tokenUnderlyingPrice = await this.getLSTExchangeRate();
|
|
367
|
-
// // to offset for usual DEX lag, we multiply by 0.998 (i.e. 0.2% loss)
|
|
368
|
-
// tokenUnderlyingPrice = tokenUnderlyingPrice * 0.998;
|
|
369
|
-
// logger.verbose(`${this.getTag()} tokenUnderlyingPrice: ${tokenUnderlyingPrice}`);
|
|
370
|
-
|
|
371
|
-
// // handle collateral
|
|
372
|
-
// if (legAUM[0].token.address.eq(underlying.address)) {
|
|
373
|
-
// vesuAum = vesuAum.plus(legAUM[0].amount);
|
|
374
|
-
// } else {
|
|
375
|
-
// vesuAum = vesuAum.plus(legAUM[0].amount.dividedBy(tokenUnderlyingPrice));
|
|
376
|
-
// }
|
|
377
|
-
|
|
378
|
-
// // handle debt
|
|
379
|
-
// if (legAUM[1].token.address.eq(underlying.address)) {
|
|
380
|
-
// vesuAum = vesuAum.minus(legAUM[1].amount);
|
|
381
|
-
// } else {
|
|
382
|
-
// vesuAum = vesuAum.minus(legAUM[1].amount.dividedBy(tokenUnderlyingPrice));
|
|
383
|
-
// };
|
|
384
|
-
|
|
385
|
-
// logger.verbose(`${this.getTag()} Vesu AUM: ${vesuAum}, legCollateral: ${legAUM[0].amount.toNumber()}, legDebt: ${legAUM[1].amount.toNumber()}`);
|
|
386
|
-
// return vesuAum;
|
|
387
|
-
// }
|
|
388
|
-
|
|
389
|
-
//
|
|
390
|
-
// private async _getMinOutputAmountLSTBuy(amountInUnderlying: Web3Number) {
|
|
391
|
-
// const lstTruePrice = await this.getLSTExchangeRate();
|
|
392
|
-
// // during buy, the purchase should always be <= true LST price.
|
|
393
|
-
// const minOutputAmount = amountInUnderlying.dividedBy(lstTruePrice).multipliedBy(0.99979); // minus 0.021% to account for avnu fees
|
|
394
|
-
// return new Web3Number(minOutputAmount.toString(), this.asset().decimals);
|
|
395
|
-
// }
|
|
396
455
|
|
|
397
|
-
// private async _getMinOutputAmountLSTSell(amountInLST: Web3Number) {
|
|
398
|
-
// const lstTruePrice = await this.getLSTExchangeRate();
|
|
399
|
-
// // during sell, the purchase should always be > 0.995 * true LST price.
|
|
400
|
-
// const minOutputAmount = amountInLST.multipliedBy(lstTruePrice).multipliedBy(0.995);
|
|
401
|
-
// return minOutputAmount;
|
|
402
|
-
// }
|
|
403
456
|
|
|
404
457
|
|
|
405
458
|
|
|
406
459
|
// todo add a function to findout max borrowable amount without fucking yield
|
|
407
|
-
// if the current net yield < LST yield, add a function to calculate how much to unwind.
|
|
460
|
+
// if the current net yield < LST yield, add a function to calculate how much to unwind.
|
|
408
461
|
|
|
409
462
|
/**
|
|
410
463
|
* Uses vesu's multiple call to create leverage on LST
|
|
411
464
|
* Deposit amount is in LST
|
|
412
|
-
* @param params
|
|
465
|
+
* @param params
|
|
413
466
|
*/
|
|
414
467
|
async getFundManagementCall(params: {
|
|
415
468
|
isDeposit: boolean,
|
|
@@ -420,8 +473,8 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
420
473
|
// logger.verbose(`${this.getTag()}::getVesuMultiplyCall legLTV: ${legLTV}`);
|
|
421
474
|
const allAdapters = this.metadata.additionalInfo.adapters.map(adapter => adapter.adapter);
|
|
422
475
|
if (!params.isDeposit) {
|
|
423
|
-
// try using unused balance to unwind.
|
|
424
|
-
// no need to unwind.
|
|
476
|
+
// try using unused balance to unwind.
|
|
477
|
+
// no need to unwind.
|
|
425
478
|
const unusedBalance = await this.getUnusedBalance();
|
|
426
479
|
logger.verbose(`${this.getTag()}::getVesuMultiplyCall unusedBalance: ${unusedBalance.amount.toString()}, required: ${params.leg1DepositAmount.toString()}`);
|
|
427
480
|
if (unusedBalance.amount.gte(params.leg1DepositAmount)) {
|
|
@@ -432,8 +485,8 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
432
485
|
const proofsInfo = adapters.map(adapter => adapter.getProofs(false, this.getMerkleTree()));
|
|
433
486
|
const calls: Call[] = [];
|
|
434
487
|
for (const info of proofsInfo) {
|
|
435
|
-
const
|
|
436
|
-
const call = this.getManageCall(
|
|
488
|
+
const manageCalls = await info.callConstructor({ amount: params.leg1DepositAmount });
|
|
489
|
+
const call = this.getManageCall(this.getProofGroupsForManageCalls(manageCalls), manageCalls);
|
|
437
490
|
calls.push(call);
|
|
438
491
|
}
|
|
439
492
|
return calls;
|
|
@@ -448,8 +501,8 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
448
501
|
const proofsInfo = adapters.map(adapter => adapter.getProofs(true, this.getMerkleTree()));
|
|
449
502
|
const calls: Call[] = [];
|
|
450
503
|
for (const info of proofsInfo) {
|
|
451
|
-
const
|
|
452
|
-
const call = this.getManageCall(
|
|
504
|
+
const manageCalls = await info.callConstructor({ amount: params.leg1DepositAmount });
|
|
505
|
+
const call = this.getManageCall(this.getProofGroupsForManageCalls(manageCalls), manageCalls);
|
|
453
506
|
calls.push(call);
|
|
454
507
|
}
|
|
455
508
|
return calls;
|
|
@@ -461,11 +514,11 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
461
514
|
// const collateralisation = await vesuAdapter1.getCollateralization(this.config);
|
|
462
515
|
// const existingCollateralInfo = existingPositions[0];
|
|
463
516
|
// const existingDebtInfo = existingPositions[1];
|
|
464
|
-
// logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
517
|
+
// logger.debug(`${this.getTag()}::getVesuMultiplyCall existingCollateralInfo: ${JSON.stringify(existingCollateralInfo)},
|
|
465
518
|
// existingDebtInfo: ${JSON.stringify(existingDebtInfo)}, collateralisation: ${JSON.stringify(collateralisation)}`);
|
|
466
519
|
|
|
467
520
|
// // - Prices as seen by Vesu contracts, ideal for HF math
|
|
468
|
-
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
521
|
+
// // Price 1 is ok as fallback bcz that would relatively price the
|
|
469
522
|
// // collateral and debt as equal.
|
|
470
523
|
// const collateralPrice = collateralisation[0].usdValue > 0 ? collateralisation[0].usdValue / existingCollateralInfo.amount.toNumber() : 1;
|
|
471
524
|
// const debtPrice = collateralisation[1].usdValue > 0 ? collateralisation[1].usdValue / existingDebtInfo.amount.toNumber() : 1;
|
|
@@ -476,7 +529,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
476
529
|
|
|
477
530
|
// // compute optimal amount of collateral and debt post addition/removal
|
|
478
531
|
// // target hf = collateral * collateralPrice * ltv / debt * debtPrice
|
|
479
|
-
// // assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
532
|
+
// // assuming X to be the usd amount of debt borrowed or repaied (negative).
|
|
480
533
|
// // target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv / (debt * debtPrice + X)
|
|
481
534
|
// // => X * target hf = (((collateral + legDepositAmount) * collateralPrice + X)) * ltv - (debt * debtPrice * target hf)
|
|
482
535
|
// // => X * (target hf - ltv)= ((collateral + legDepositAmount) * collateralPrice * ltv) - (debt * debtPrice * target hf)
|
|
@@ -519,149 +572,166 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
519
572
|
return this.metadata.additionalInfo.underlyingToken;
|
|
520
573
|
}
|
|
521
574
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
575
|
+
async getLSTAPR(_address: ContractAddr): Promise<number> {
|
|
576
|
+
try {
|
|
577
|
+
const vesuAdapter1 = this.getVesuSameTokenAdapter();
|
|
578
|
+
const apr = await LSTAPRService.getLSTAPR(vesuAdapter1._vesuAdapter.config.debt.address);
|
|
579
|
+
if (!apr) {
|
|
580
|
+
throw new Error('Failed to get LST APR');
|
|
581
|
+
}
|
|
582
|
+
return apr;
|
|
583
|
+
} catch (error) {
|
|
584
|
+
logger.warn(`${this.getTag()}: Failed to get LST APR: ${error}`);
|
|
585
|
+
return 0;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
533
588
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
589
|
+
async netAPY(): Promise<{
|
|
590
|
+
net: number;
|
|
591
|
+
splits: { apy: number; id: string }[];
|
|
592
|
+
}> {
|
|
593
|
+
const unusedBalance = await this.getUnusedBalance();
|
|
594
|
+
const maxDeposits = await this.maxNewDeposits({ isAPYComputation: true });
|
|
595
|
+
const lstAPY = await this.getLSTAPR(this.getLSTUnderlyingTokenInfo().address);
|
|
596
|
+
|
|
597
|
+
if (maxDeposits * 1.5 < unusedBalance.amount.toNumber()) {
|
|
598
|
+
logger.verbose(
|
|
599
|
+
`${this.getTag()}::netAPY: unused balance is > max servicable from loan, lstAPY: ${lstAPY}`,
|
|
600
|
+
);
|
|
601
|
+
const output = await super.netAPY();
|
|
602
|
+
output.splits.push({ apy: lstAPY, id: "lst_apy" });
|
|
603
|
+
return output;
|
|
604
|
+
} else {
|
|
605
|
+
logger.verbose(
|
|
606
|
+
`${this.getTag()}::netAPY: we can take more deposits, use theoretical max APY`,
|
|
607
|
+
);
|
|
608
|
+
const output = await super.netAPY();
|
|
609
|
+
output.splits.push({ apy: lstAPY, id: "lst_apy" });
|
|
610
|
+
return output;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
551
613
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
614
|
+
async maxNewDeposits(
|
|
615
|
+
params: { isAPYComputation: boolean } = { isAPYComputation: false },
|
|
616
|
+
) {
|
|
617
|
+
let numerator = 0;
|
|
618
|
+
for (let adapter of this.getVesuAdapters()) {
|
|
619
|
+
const maxDepositInfo = await adapter.maxDeposit();
|
|
620
|
+
const ltv = await adapter._vesuAdapter.getLTVConfig(this.config);
|
|
621
|
+
numerator +=
|
|
622
|
+
(this.metadata.additionalInfo.targetHealthFactor *
|
|
623
|
+
maxDepositInfo.amount.toNumber()) / ltv;
|
|
624
|
+
}
|
|
625
|
+
return numerator;
|
|
626
|
+
}
|
|
557
627
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
// } catch (error) {
|
|
568
|
-
// logger.warn(`${this.getTag()}: Failed to get max swappable: ${error}`);
|
|
569
|
-
// const maxSwappable = Web3Number.fromWei("0", vesuAdapter.config.debt.decimals);
|
|
570
|
-
// return {amount: maxBorrowable.minimum(maxSwappable), dexSwappableAmount: maxSwappable, maxBorrowableAmount: maxBorrowable, borrowableAsset: vesuAdapter.config.debt};
|
|
571
|
-
// }
|
|
572
|
-
// }
|
|
628
|
+
protected async getUnusedBalanceAPY() {
|
|
629
|
+
const unusedBalance = await this.getUnusedBalance();
|
|
630
|
+
const underlying = this.getLSTUnderlyingTokenInfo();
|
|
631
|
+
const lstAPY = await this.getLSTAPR(underlying.address);
|
|
632
|
+
return {
|
|
633
|
+
apy: lstAPY,
|
|
634
|
+
weight: unusedBalance.usdValue,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
573
637
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
// /**
|
|
579
|
-
// * Gets LST APR for the strategy's underlying asset from Endur API
|
|
580
|
-
// * @returns Promise<number> The LST APR (not divided by 1e18)
|
|
581
|
-
// */
|
|
582
|
-
// async getLSTAPR(_address: ContractAddr): Promise<number> {
|
|
583
|
-
// try {
|
|
584
|
-
// const vesuAdapter1 = this.getVesuSameTokenAdapter();
|
|
585
|
-
// const apr = await LSTAPRService.getLSTAPR(vesuAdapter1.config.debt.address);
|
|
586
|
-
// if (!apr) {
|
|
587
|
-
// throw new Error('Failed to get LST APR');
|
|
588
|
-
// }
|
|
589
|
-
// return apr;
|
|
590
|
-
// } catch (error) {
|
|
591
|
-
// logger.warn(`${this.getTag()}: Failed to get LST APR: ${error}`);
|
|
592
|
-
// return 0;
|
|
593
|
-
// }
|
|
594
|
-
// }
|
|
638
|
+
async getAUM(unrealizedAUM: boolean = false): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
|
|
639
|
+
const underlying = this.asset();
|
|
640
|
+
assert(underlying.symbol.startsWith('x'), 'Underlying is not an LST of Endur');
|
|
595
641
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
642
|
+
const priceType = unrealizedAUM ? LSTPriceType.ENDUR_PRICE : LSTPriceType.AVNU_PRICE;
|
|
643
|
+
|
|
644
|
+
let tokenUnderlyingPrice: number;
|
|
645
|
+
if (priceType === LSTPriceType.ENDUR_PRICE) {
|
|
646
|
+
tokenUnderlyingPrice = await this.getLSTExchangeRate();
|
|
647
|
+
if (tokenUnderlyingPrice === 0) {
|
|
648
|
+
throw new Error(`${this.getTag()}::getAUM: tokenUnderlyingPrice (Endur) is 0`);
|
|
649
|
+
}
|
|
650
|
+
const avnuRate = await this.getLSTAvnuRate();
|
|
651
|
+
if (avnuRate === 0) {
|
|
652
|
+
throw new Error(`${this.getTag()}::getAUM: tokenUnderlyingPrice (Avnu) is 0`);
|
|
653
|
+
}
|
|
654
|
+
const diff = Math.abs(tokenUnderlyingPrice - avnuRate) / tokenUnderlyingPrice;
|
|
655
|
+
if (diff > 0.02) {
|
|
656
|
+
throw new Error(
|
|
657
|
+
`${this.getTag()}::getAUM: Endur and Avnu prices differ by more than 2% (Endur: ${tokenUnderlyingPrice}, Avnu: ${avnuRate})`,
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
logger.verbose(`${this.getTag()} tokenUnderlyingPrice (Endur): ${tokenUnderlyingPrice}, avnuRate: ${avnuRate}, diff: ${diff}`);
|
|
661
|
+
} else {
|
|
662
|
+
tokenUnderlyingPrice = await this.getLSTAvnuRate();
|
|
663
|
+
if (tokenUnderlyingPrice === 0) {
|
|
664
|
+
throw new Error(`${this.getTag()}::getAUM: tokenUnderlyingPrice (Avnu) is 0`);
|
|
665
|
+
}
|
|
666
|
+
logger.verbose(`${this.getTag()} tokenUnderlyingPrice (Avnu): ${tokenUnderlyingPrice}`);
|
|
667
|
+
}
|
|
619
668
|
|
|
620
|
-
async getAUM(): Promise<{net: SingleTokenInfo, prevAum: Web3Number, splits: PositionInfo[]}> {
|
|
621
669
|
const allPositions: PositionInfo[] = [];
|
|
622
670
|
for (let adapter of this.metadata.additionalInfo.adapters) {
|
|
623
671
|
const positions = await adapter.adapter.getPositions();
|
|
624
672
|
allPositions.push(...positions);
|
|
625
673
|
}
|
|
626
674
|
|
|
627
|
-
|
|
628
|
-
let netAUM = new Web3Number(0, this.asset().decimals);
|
|
675
|
+
let netAUM = new Web3Number(0, underlying.decimals);
|
|
629
676
|
for (let position of allPositions) {
|
|
630
|
-
if (position.tokenInfo.address.eq(
|
|
677
|
+
if (position.tokenInfo.address.eq(underlying.address)) {
|
|
631
678
|
netAUM = netAUM.plus(position.amount);
|
|
632
679
|
} else {
|
|
633
|
-
netAUM = netAUM.plus(position.
|
|
680
|
+
netAUM = netAUM.plus(position.amount.dividedBy(tokenUnderlyingPrice));
|
|
634
681
|
}
|
|
635
682
|
}
|
|
636
683
|
|
|
684
|
+
const assetPrice = await this.pricer.getPrice(underlying.symbol);
|
|
637
685
|
const prevAum = await this.getPrevAUM();
|
|
686
|
+
const priceTypeLabel = priceType === LSTPriceType.ENDUR_PRICE ? "Endur Price" : "Avnu Price";
|
|
687
|
+
logger.verbose(`${this.getTag()} AUM (${priceTypeLabel}): ${netAUM}`);
|
|
688
|
+
|
|
638
689
|
const realAUM: PositionInfo = {
|
|
639
|
-
tokenInfo:
|
|
690
|
+
tokenInfo: underlying,
|
|
640
691
|
amount: netAUM,
|
|
641
692
|
usdValue: netAUM.toNumber() * assetPrice.price,
|
|
642
693
|
apy: {apy: netAUM.toNumber() * assetPrice.price, type: APYType.BASE},
|
|
643
694
|
remarks: AUMTypes.FINALISED,
|
|
644
|
-
protocol: Protocols.NONE
|
|
695
|
+
protocol: Protocols.NONE
|
|
645
696
|
};
|
|
646
697
|
|
|
647
698
|
const estimatedAUMDelta: PositionInfo = {
|
|
648
|
-
tokenInfo:
|
|
649
|
-
amount: Web3Number.fromWei('0',
|
|
699
|
+
tokenInfo: underlying,
|
|
700
|
+
amount: Web3Number.fromWei('0', underlying.decimals),
|
|
650
701
|
usdValue: 0,
|
|
651
702
|
apy: {apy: 0, type: APYType.BASE},
|
|
652
703
|
remarks: AUMTypes.DEFISPRING,
|
|
653
|
-
protocol: Protocols.NONE
|
|
704
|
+
protocol: Protocols.NONE
|
|
654
705
|
};
|
|
655
706
|
|
|
656
707
|
return {net: {
|
|
657
|
-
tokenInfo:
|
|
708
|
+
tokenInfo: underlying,
|
|
658
709
|
amount: netAUM,
|
|
659
710
|
usdValue: netAUM.toNumber() * assetPrice.price
|
|
660
711
|
}, prevAum: prevAum, splits: [realAUM, estimatedAUMDelta]};
|
|
661
712
|
}
|
|
662
713
|
|
|
714
|
+
async getTVLUnrealized() {
|
|
715
|
+
return await this.getAUM(true);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
async getUserUnrealizedGains(user: ContractAddr) {
|
|
719
|
+
const tvl = await this.getTVL();
|
|
720
|
+
const unrealizedTVL = await this.getTVLUnrealized();
|
|
721
|
+
const unrealizedDiff = unrealizedTVL.net.amount.minus(tvl.amount);
|
|
722
|
+
const userTVL = await this.getUserTVL(user);
|
|
723
|
+
const userShare = userTVL.amount.dividedBy(tvl.amount);
|
|
724
|
+
const unrealizedGains = unrealizedDiff.multipliedBy(userShare);
|
|
725
|
+
|
|
726
|
+
return {
|
|
727
|
+
unrealizedGains,
|
|
728
|
+
userShare: userShare.toNumber(),
|
|
729
|
+
tokenInfo: this.asset(),
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
|
|
663
733
|
/**
|
|
664
|
-
*
|
|
734
|
+
*
|
|
665
735
|
* @param params marginAmount is in LST, debtAmount is in underlying
|
|
666
736
|
*/
|
|
667
737
|
// async getModifyLeverCall(params: {
|
|
@@ -671,7 +741,7 @@ export class UniversalLstMultiplierStrategy<S extends HyperLSTStrategySettings>
|
|
|
671
741
|
// isIncrease: boolean
|
|
672
742
|
// }): Promise<Call[]> {
|
|
673
743
|
// logger.verbose(`${this.getTag()}::getModifyLeverCall marginAmount: ${params.marginAmount}, debtAmount: ${params.debtAmount}, lstDexPriceInUnderlying: ${params.lstDexPriceInUnderlying}, isIncrease: ${params.isIncrease}`);
|
|
674
|
-
|
|
744
|
+
|
|
675
745
|
// const vesuAdapter = this.getVesuSameTokenAdapter();
|
|
676
746
|
// const manage0Info = vesuAdapter.
|
|
677
747
|
// const manageCall0 = manage0Info.callConstructor({
|
|
@@ -689,7 +759,6 @@ export default function VaultDescription(
|
|
|
689
759
|
const containerStyle = {
|
|
690
760
|
maxWidth: "800px",
|
|
691
761
|
margin: "0 auto",
|
|
692
|
-
backgroundColor: "#111",
|
|
693
762
|
color: "#eee",
|
|
694
763
|
fontFamily: "Arial, sans-serif",
|
|
695
764
|
borderRadius: "12px",
|
|
@@ -716,7 +785,7 @@ export default function VaultDescription(
|
|
|
716
785
|
</div>
|
|
717
786
|
<div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
|
|
718
787
|
<p style={{ fontSize: "13px", color: "#ccc" }}>
|
|
719
|
-
<strong>Debt limits:</strong> Pools on Vesu have debt caps that are gradually increased over time. Until caps are raised, deposited
|
|
788
|
+
<strong>Debt limits:</strong> Pools on Vesu have debt caps that are gradually increased over time. Until caps are raised, deposited LSTs remain in the vault, generating a shared net return for all depositors. There is no additional fee taken by Troves on LST APY, its only on added gain.
|
|
720
789
|
</p>
|
|
721
790
|
</div>
|
|
722
791
|
{/* <div style={{ backgroundColor: "#222", padding: "10px", borderRadius: "8px", marginBottom: "20px", border: "1px solid #444" }}>
|
|
@@ -782,21 +851,25 @@ function getLooperSettings(
|
|
|
782
851
|
poolId: pool1,
|
|
783
852
|
collateral: lstToken,
|
|
784
853
|
debt: Global.getDefaultTokens().find(token => token.symbol === position)!,
|
|
854
|
+
marginToken: lstToken,
|
|
785
855
|
targetHealthFactor: vaultSettings.targetHealthFactor,
|
|
786
856
|
minHealthFactor: vaultSettings.minHealthFactor,
|
|
787
857
|
quoteAmountToFetchPrice: vaultSettings.quoteAmountToFetchPrice,
|
|
788
858
|
...baseAdapterConfig,
|
|
789
859
|
supportedPositions: [{asset: lstToken, isDebt: false}, {asset: Global.getDefaultTokens().find(token => token.symbol === position)!, isDebt: true}],
|
|
790
|
-
minimumVesuMovementAmount: 0
|
|
860
|
+
minimumVesuMovementAmount: 0
|
|
791
861
|
}));
|
|
792
862
|
|
|
793
|
-
|
|
863
|
+
// Tracks LST balance in the vault allocator; fromAddress=vaultAllocator, toAddress=vaultAddress
|
|
864
|
+
const lstTransferAdapter = new TokenTransferAdapter({
|
|
794
865
|
...baseAdapterConfig,
|
|
866
|
+
fromAddress: vaultSettings.vaultAllocator,
|
|
867
|
+
toAddress: vaultSettings.vaultAddress,
|
|
795
868
|
});
|
|
796
869
|
|
|
797
870
|
vaultSettings.adapters.push({id: `${vesuAdapterLST.name}_${lstToken.symbol}_${underlyingToken.symbol}`, adapter: vesuAdapterLST});
|
|
798
871
|
vesuMultiplyAdapters.map(adapter => vaultSettings.adapters.push({id: `${adapter.name}_${lstToken.symbol}_${underlyingToken.symbol}`, adapter: adapter}));
|
|
799
|
-
vaultSettings.adapters.push({id: `${
|
|
872
|
+
vaultSettings.adapters.push({id: `${lstTransferAdapter.name}_${lstToken.symbol}`, adapter: lstTransferAdapter});
|
|
800
873
|
|
|
801
874
|
const commonAdapter = new CommonAdapter({
|
|
802
875
|
id: UNIVERSAL_MANAGE_IDS.FLASH_LOAN,
|
|
@@ -813,11 +886,11 @@ function getLooperSettings(
|
|
|
813
886
|
// push vesu multiply adapter to leaf adapters
|
|
814
887
|
vesuMultiplyAdapters.map(adapter => vaultSettings.leafAdapters.push(() => adapter.getDepositLeaf()));
|
|
815
888
|
vesuMultiplyAdapters.map(adapter => vaultSettings.leafAdapters.push(() => adapter.getWithdrawLeaf()));
|
|
816
|
-
|
|
889
|
+
|
|
817
890
|
// to bridge liquidity back to vault (used by bring_liquidity)
|
|
818
891
|
vaultSettings.leafAdapters.push(commonAdapter.getApproveAdapter(lstToken.address, vaultSettings.vaultAddress, UNIVERSAL_MANAGE_IDS.APPROVE_BRING_LIQUIDITY).bind(commonAdapter));
|
|
819
892
|
vaultSettings.leafAdapters.push(commonAdapter.getBringLiquidityAdapter(UNIVERSAL_MANAGE_IDS.BRING_LIQUIDITY).bind(commonAdapter));
|
|
820
|
-
|
|
893
|
+
|
|
821
894
|
// claim rewards
|
|
822
895
|
// vaultSettings.leafAdapters.push(vesuAdapterLST.getDefispringRewardsAdapter(UNIVERSAL_MANAGE_IDS.DEFISPRING_REWARDS).bind(vesuAdapterLST));
|
|
823
896
|
|
|
@@ -922,9 +995,10 @@ const hyperxWBTC: HyperLSTStrategySettings = {
|
|
|
922
995
|
adapters: [],
|
|
923
996
|
targetHealthFactor: 1.1,
|
|
924
997
|
minHealthFactor: 1.05,
|
|
925
|
-
borrowable_assets:
|
|
998
|
+
borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'WBTC'),
|
|
926
999
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!,
|
|
927
1000
|
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'WBTC')!.decimals),
|
|
1001
|
+
redemptionRouter: ContractAddr.from('0x6ea649f402898f69baf775c1afdd08522c071c640b9c4460192070ec2b96417'),
|
|
928
1002
|
}
|
|
929
1003
|
|
|
930
1004
|
const hyperxtBTC: HyperLSTStrategySettings = {
|
|
@@ -937,9 +1011,10 @@ const hyperxtBTC: HyperLSTStrategySettings = {
|
|
|
937
1011
|
adapters: [],
|
|
938
1012
|
targetHealthFactor: 1.1,
|
|
939
1013
|
minHealthFactor: 1.05,
|
|
940
|
-
borrowable_assets:
|
|
1014
|
+
borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'tBTC' || token.symbol === 'WBTC'),
|
|
941
1015
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!,
|
|
942
1016
|
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'tBTC')!.decimals),
|
|
1017
|
+
redemptionRouter: ContractAddr.from('0x3de9c409d1e357e25778fb7a3e2e2393666956846a5c2caa607296fa8e76b5d'),
|
|
943
1018
|
}
|
|
944
1019
|
|
|
945
1020
|
const hyperxsBTC: HyperLSTStrategySettings = {
|
|
@@ -952,7 +1027,7 @@ const hyperxsBTC: HyperLSTStrategySettings = {
|
|
|
952
1027
|
adapters: [],
|
|
953
1028
|
targetHealthFactor: 1.1,
|
|
954
1029
|
minHealthFactor: 1.05,
|
|
955
|
-
borrowable_assets:
|
|
1030
|
+
borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'solvBTC'),
|
|
956
1031
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!,
|
|
957
1032
|
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'solvBTC')!.decimals),
|
|
958
1033
|
}
|
|
@@ -967,7 +1042,7 @@ const hyperxLBTC: HyperLSTStrategySettings = {
|
|
|
967
1042
|
adapters: [],
|
|
968
1043
|
targetHealthFactor: 1.1,
|
|
969
1044
|
minHealthFactor: 1.05,
|
|
970
|
-
borrowable_assets:
|
|
1045
|
+
borrowable_assets: Global.getDefaultTokens().filter(token => token.symbol === 'LBTC'),
|
|
971
1046
|
underlyingToken: Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!,
|
|
972
1047
|
quoteAmountToFetchPrice: new Web3Number('0.001', Global.getDefaultTokens().find(token => token.symbol === 'LBTC')!.decimals),
|
|
973
1048
|
}
|
|
@@ -1012,35 +1087,152 @@ export function getInvestmentSteps(lstSymbol: string, underlyingSymbol: string)
|
|
|
1012
1087
|
`The vault manager loops the ${underlyingSymbol} to buy ${lstSymbol}`,
|
|
1013
1088
|
`The vault manager collateralizes the ${lstSymbol} on Vesu`,
|
|
1014
1089
|
`The vault manager borrows more ${underlyingSymbol} to loop further`,
|
|
1015
|
-
`If required, adjust leverage or re-allocate assets within pool on Vesu to optimize yield`
|
|
1090
|
+
`If required, adjust leverage or re-allocate assets within LST pool on Vesu to optimize yield`
|
|
1016
1091
|
]
|
|
1017
1092
|
}
|
|
1018
1093
|
|
|
1019
|
-
function
|
|
1094
|
+
function getMaxTVL(lstSymbol: string): Web3Number {
|
|
1095
|
+
const lstMaxTVLs: Record<string, number> = {
|
|
1096
|
+
xWBTC: 5,
|
|
1097
|
+
xLBTC: 5,
|
|
1098
|
+
xtBTC: 5,
|
|
1099
|
+
xsBTC: 5,
|
|
1100
|
+
xSTRK: 7000000,
|
|
1101
|
+
};
|
|
1102
|
+
const maxTVLValue = lstMaxTVLs[lstSymbol] || 0;
|
|
1103
|
+
const token = Global.getDefaultTokens().find(
|
|
1104
|
+
(token) => token.symbol === lstSymbol,
|
|
1105
|
+
);
|
|
1106
|
+
if (!token) {
|
|
1107
|
+
return Web3Number.fromWei(0, 18);
|
|
1108
|
+
}
|
|
1109
|
+
return Web3Number.fromWei(maxTVLValue, token.decimals);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
function createHyperLSTSettings(
|
|
1113
|
+
lstSymbol: string,
|
|
1114
|
+
underlyingSymbol: string,
|
|
1115
|
+
): StrategySettings {
|
|
1116
|
+
const depositToken = Global.getDefaultTokens().find(
|
|
1117
|
+
(token) => token.symbol === lstSymbol,
|
|
1118
|
+
)!;
|
|
1119
|
+
return {
|
|
1120
|
+
maxTVL: getMaxTVL(lstSymbol),
|
|
1121
|
+
isPaused: false,
|
|
1122
|
+
liveStatus: StrategyLiveStatus.HOT,
|
|
1123
|
+
isAudited: true,
|
|
1124
|
+
isInstantWithdrawal: false,
|
|
1125
|
+
hideHarvestInfo: true,
|
|
1126
|
+
quoteToken: depositToken,
|
|
1127
|
+
showWithdrawalWarningModal: false,
|
|
1128
|
+
alerts: [
|
|
1129
|
+
{
|
|
1130
|
+
tab: "withdraw" as const,
|
|
1131
|
+
text: "On withdrawal, you will receive an NFT representing your withdrawal request. The funds will be automatically sent to your wallet (NFT owner) in 24 hours (In this initial phase of Launch). You can monitor the status in transactions tab.",
|
|
1132
|
+
type: "info" as const,
|
|
1133
|
+
},
|
|
1134
|
+
{
|
|
1135
|
+
tab: "deposit" as const,
|
|
1136
|
+
text: (
|
|
1137
|
+
<>
|
|
1138
|
+
To acquire the LST, please visit{" "}
|
|
1139
|
+
<a href="https://app.endur.fi" target="_blank" rel="noopener noreferrer">endur.fi</a>
|
|
1140
|
+
</>
|
|
1141
|
+
),
|
|
1142
|
+
type: "info" as const,
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
tab: "deposit" as const,
|
|
1146
|
+
text: "It may take up to one week for your deposit to appreciate in value. This delay occurs because the LST price is sourced from DEXes and liquidity is usually rebased once a week.",
|
|
1147
|
+
type: "info" as const,
|
|
1148
|
+
},
|
|
1149
|
+
],
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
const HYPER_LST_SECURITY = {
|
|
1154
|
+
auditStatus: AuditStatus.AUDITED,
|
|
1155
|
+
sourceCode: {
|
|
1156
|
+
type: SourceCodeType.CLOSED_SOURCE,
|
|
1157
|
+
contractLink: "https://github.com/trovesfi/troves-contracts",
|
|
1158
|
+
},
|
|
1159
|
+
accessControl: {
|
|
1160
|
+
type: AccessControlType.STANDARD_ACCOUNT,
|
|
1161
|
+
addresses: [ContractAddr.from("0x0")],
|
|
1162
|
+
timeLock: "2 Days",
|
|
1163
|
+
},
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
const HYPER_LST_REDEMPTION_INFO: RedemptionInfo = {
|
|
1167
|
+
instantWithdrawalVault: InstantWithdrawalVault.NO,
|
|
1168
|
+
redemptionsInfo: [
|
|
1169
|
+
{
|
|
1170
|
+
title: "Typical Duration",
|
|
1171
|
+
description: "1-2 hours",
|
|
1172
|
+
},
|
|
1173
|
+
],
|
|
1174
|
+
alerts: [
|
|
1175
|
+
{
|
|
1176
|
+
type: "info",
|
|
1177
|
+
text: "In cases of low liquidity, high slippages, the redemptions can take longer time. Redemption times are estimates and may vary based on network conditions and liquidity requirements.",
|
|
1178
|
+
tab: "withdraw",
|
|
1179
|
+
},
|
|
1180
|
+
],
|
|
1181
|
+
};
|
|
1182
|
+
|
|
1183
|
+
function getStrategySettings(
|
|
1184
|
+
lstSymbol: string,
|
|
1185
|
+
underlyingSymbol: string,
|
|
1186
|
+
settings: HyperLSTStrategySettings,
|
|
1187
|
+
isPreview: boolean = false,
|
|
1188
|
+
isLST: boolean,
|
|
1189
|
+
): IStrategyMetadata<HyperLSTStrategySettings> {
|
|
1020
1190
|
return {
|
|
1191
|
+
id: `hyper_${lstSymbol.toLowerCase()}`,
|
|
1021
1192
|
name: `Hyper ${lstSymbol}`,
|
|
1022
1193
|
description: getDescription(lstSymbol, underlyingSymbol),
|
|
1023
|
-
address:
|
|
1194
|
+
address: settings.vaultAddress,
|
|
1024
1195
|
launchBlock: 0,
|
|
1025
1196
|
type: 'Other',
|
|
1197
|
+
vaultType: {
|
|
1198
|
+
type: VaultType.LOOPING,
|
|
1199
|
+
description: `Creates leveraged looping position on ${lstSymbol} by borrowing ${underlyingSymbol} to increase yield`,
|
|
1200
|
+
},
|
|
1026
1201
|
depositTokens: [Global.getDefaultTokens().find(token => token.symbol === lstSymbol)!],
|
|
1027
|
-
additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol,
|
|
1202
|
+
additionalInfo: getLooperSettings(lstSymbol, underlyingSymbol, settings, lstSymbol === 'xSTRK' ? VesuPools.Re7xSTRK : VesuPools.Re7xBTC),
|
|
1028
1203
|
risk: {
|
|
1029
1204
|
riskFactor: _riskFactor,
|
|
1030
1205
|
netRisk:
|
|
1031
1206
|
_riskFactor.reduce((acc, curr) => acc + curr.value * curr.weight, 0) /
|
|
1032
1207
|
_riskFactor.reduce((acc, curr) => acc + curr.weight, 0),
|
|
1033
|
-
notARisks: getNoRiskTags(_riskFactor)
|
|
1208
|
+
notARisks: getNoRiskTags(_riskFactor),
|
|
1034
1209
|
},
|
|
1035
1210
|
auditUrl: AUDIT_URL,
|
|
1036
1211
|
protocols: [Protocols.ENDUR, Protocols.VESU],
|
|
1037
|
-
|
|
1038
|
-
|
|
1212
|
+
curator: {
|
|
1213
|
+
name: "Unwrap Labs",
|
|
1214
|
+
logo: "https://assets.troves.fi/integrations/unwraplabs/white.png",
|
|
1215
|
+
},
|
|
1216
|
+
settings: createHyperLSTSettings(lstSymbol, underlyingSymbol),
|
|
1217
|
+
contractDetails: getContractDetails(settings),
|
|
1039
1218
|
faqs: getFAQs(lstSymbol, underlyingSymbol, isLST),
|
|
1040
1219
|
investmentSteps: getInvestmentSteps(lstSymbol, underlyingSymbol),
|
|
1041
1220
|
isPreview: isPreview,
|
|
1042
|
-
apyMethodology:
|
|
1043
|
-
|
|
1221
|
+
apyMethodology: 'Current annualized APY in terms of base asset of the LST. There is no additional fee taken by Troves on LST APY. We charge a 10% performance fee on the additional gain which is already accounted in the APY shown.',
|
|
1222
|
+
realizedAPYMethodology: 'The realizedAPY is based on past 14 days performance by the vault',
|
|
1223
|
+
tags: lstSymbol.includes('BTC')
|
|
1224
|
+
? [StrategyTag.BTC, StrategyTag.LEVERED]
|
|
1225
|
+
: [StrategyTag.LEVERED],
|
|
1226
|
+
security: HYPER_LST_SECURITY,
|
|
1227
|
+
redemptionInfo: HYPER_LST_REDEMPTION_INFO,
|
|
1228
|
+
usualTimeToEarnings: '2 weeks',
|
|
1229
|
+
usualTimeToEarningsDescription: 'Strategy returns depend on LST price on DEXes. Even though the true price of LST on Endur increases continuously, the DEX price may lag sometimes, and historically is seen to rebase at least once every 2 hours. This is when you realise your earnings.',
|
|
1230
|
+
points: lstSymbol === 'xSTRK' ? [{
|
|
1231
|
+
multiplier: 4,
|
|
1232
|
+
logo: "https://endur.fi/favicon.ico",
|
|
1233
|
+
toolTip: "This strategy holds xSTRK. Earn 3-4x Endur points on your xSTRK due to the leverage. Points can be found on endur.fi.",
|
|
1234
|
+
}] : undefined,
|
|
1235
|
+
};
|
|
1044
1236
|
}
|
|
1045
1237
|
|
|
1046
1238
|
|